1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/sendto_kdc.c */
4 * Copyright 1990,1991,2001,2002,2004,2005,2007,2008 by the Massachusetts Institute of Technology.
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.
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.
27 * MS-KKDCP implementation Copyright 2013,2014 Red Hat, Inc.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions are met:
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
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
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.
53 /* Send packet to KDC for realm; wait for response, retransmitting
58 #include "fake-addrinfo.h"
62 #if defined(HAVE_POLL_H)
65 #define MAX_POLLFDS 1024
66 #elif defined(HAVE_SYS_SELECT_H)
67 #include <sys/select.h>
72 #include <sys/ioctl.h>
73 #ifdef HAVE_SYS_FILIO_H
74 #include <sys/filio.h>
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 */
83 /* Select state flags. */
85 #define SSF_WRITE 0x02
86 #define SSF_EXCEPTION 0x04
88 typedef int64_t time_ms;
90 /* This can be pretty large, so should not be stack-allocated. */
93 struct pollfd fds[MAX_POLLFDS];
96 fd_set rfds, wfds, xfds;
101 /* connection states */
102 enum conn_states { INITIALIZING, CONNECTING, WRITING, READING, FAILED };
103 struct incoming_message {
104 size_t bufsizebytes_read;
108 unsigned char bufsizebytes[4];
112 struct outgoing_message {
116 unsigned char msg_len_buf[4];
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);
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;
136 struct conn_state *next;
140 const char *uri_path;
141 const char *servername;
142 char port[PORT_LENGTH];
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)
153 krb5_plugin_initvt_fn initfn;
156 if (context->tls != NULL)
159 context->tls = calloc(1, sizeof(*context->tls));
160 if (context->tls == NULL)
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);
167 (*initfn)(context, 0, 0, (krb5_plugin_vtable)context->tls);
169 TRACE_SENDTO_KDC_K5TLS_LOAD_ERROR(context, ret);
174 /* Get current time in milliseconds. */
175 static krb5_error_code
176 get_curtime_ms(time_ms *time_out)
182 if (gettimeofday(&tv, 0))
184 *time_out = (time_ms)tv.tv_sec * 1000 + tv.tv_usec / 1000;
189 free_http_tls_data(krb5_context context, struct conn_state *state)
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;
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)
206 for (i = 0; i < selstate->nfds; i++) {
207 if (selstate->fds[i].fd == fd)
208 return &selstate->fds[i];
214 cm_init_selstate(struct select_state *selstate)
220 cm_add_fd(struct select_state *selstate, int fd)
222 if (selstate->nfds >= MAX_POLLFDS)
224 selstate->fds[selstate->nfds].fd = fd;
225 selstate->fds[selstate->nfds].events = 0;
231 cm_remove_fd(struct select_state *selstate, int fd)
233 struct pollfd *pfd = find_pollfd(selstate, fd);
235 *pfd = selstate->fds[selstate->nfds - 1];
239 /* Poll for reading (and not writing) on fd the next time we poll. */
241 cm_read(struct select_state *selstate, int fd)
243 find_pollfd(selstate, fd)->events = POLLIN;
246 /* Poll for writing (and not reading) on fd the next time we poll. */
248 cm_write(struct select_state *selstate, int fd)
250 find_pollfd(selstate, fd)->events = POLLOUT;
253 /* Get the output events for fd in the form of ssflags. */
255 cm_get_ssflags(struct select_state *selstate, int fd)
257 struct pollfd *pfd = find_pollfd(selstate, fd);
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.
265 if (pfd->revents != 0 && !(pfd->revents & (POLLIN | POLLOUT)))
266 return SSF_EXCEPTION;
268 return ((pfd->revents & POLLIN) ? SSF_READ : 0) |
269 ((pfd->revents & POLLOUT) ? SSF_WRITE : 0) |
270 ((pfd->revents & POLLERR) ? SSF_EXCEPTION : 0);
273 #else /* not USE_POLL */
276 cm_init_selstate(struct select_state *selstate)
280 FD_ZERO(&selstate->rfds);
281 FD_ZERO(&selstate->wfds);
282 FD_ZERO(&selstate->xfds);
286 cm_add_fd(struct select_state *selstate, int fd)
288 #ifndef _WIN32 /* On Windows FD_SETSIZE is a count, not a max value. */
289 if (fd >= FD_SETSIZE)
292 FD_SET(fd, &selstate->xfds);
293 if (selstate->max <= fd)
294 selstate->max = fd + 1;
300 cm_remove_fd(struct select_state *selstate, int fd)
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))
315 /* Select for reading (and not writing) on fd the next time we select. */
317 cm_read(struct select_state *selstate, int fd)
319 FD_SET(fd, &selstate->rfds);
320 FD_CLR(fd, &selstate->wfds);
323 /* Select for writing (and not reading) on fd the next time we select. */
325 cm_write(struct select_state *selstate, int fd)
327 FD_CLR(fd, &selstate->rfds);
328 FD_SET(fd, &selstate->wfds);
331 /* Get the events for fd from selstate after a select. */
333 cm_get_ssflags(struct select_state *selstate, int fd)
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);
340 #endif /* not USE_POLL */
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)
349 krb5_error_code retval;
350 time_ms curtime, interval;
352 retval = get_curtime_ms(&curtime);
355 interval = (curtime < endtime) ? endtime - curtime : 0;
357 /* We don't need a separate copy of the selstate for poll, but use one for
358 * consistency with how we use select. */
362 *sret = poll(out->fds, out->nfds, interval);
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);
369 return (*sret < 0) ? SOCKET_ERRNO : 0;
373 socktype_for_transport(k5_transport transport)
387 check_for_svc_unavailable (krb5_context context,
388 const krb5_data *reply,
389 void *msg_handler_data)
391 krb5_error_code *retval = (krb5_error_code *)msg_handler_data;
395 if (krb5_is_krb_error(reply)) {
396 krb5_error *err_reply;
398 if (decode_krb5_error(reply, &err_reply) == 0) {
399 *retval = err_reply->error;
400 krb5_free_error(context, err_reply);
402 /* Returning 0 means continue to next KDC */
403 return (*retval != KDC_ERR_SVC_UNAVAILABLE);
411 krb5_set_kdc_send_hook(krb5_context context, krb5_pre_send_fn send_hook,
414 context->kdc_send_hook = send_hook;
415 context->kdc_send_hook_data = data;
419 krb5_set_kdc_recv_hook(krb5_context context, krb5_post_recv_fn recv_hook,
422 context->kdc_recv_hook = recv_hook;
423 context->kdc_recv_hook_data = data;
427 * send the formatted request 'message' to a KDC for realm 'realm' and
428 * return the response (if any) in 'reply'.
430 * If the message is sent and a response is received, 0 is returned,
431 * otherwise an error code is returned.
433 * The storage for 'reply' is allocated and should be freed by the caller
438 krb5_sendto_kdc(krb5_context context, const krb5_data *message,
439 const krb5_data *realm, krb5_data *reply_out, int *use_master,
442 krb5_error_code retval, oldret, err;
443 struct serverlist servers;
445 k5_transport_strategy strategy;
446 krb5_data reply = empty_data(), *hook_message = NULL, *hook_reply = NULL;
448 *reply_out = empty_data();
451 * find KDC location(s) for realm
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.
463 TRACE_SENDTO_KDC(context, message->length, realm, *use_master, no_udp);
465 if (!no_udp && context->udp_pref_limit < 0) {
467 retval = profile_get_integer(context->profile,
468 KRB5_CONF_LIBDEFAULTS, KRB5_CONF_UDP_PREFERENCE_LIMIT, 0,
469 DEFAULT_UDP_PREF_LIMIT, &tmp);
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
478 tmp = HARD_UDP_LIMIT;
479 context->udp_pref_limit = tmp;
484 else if (message->length <= (unsigned int) context->udp_pref_limit)
485 strategy = UDP_FIRST;
489 retval = k5_locate_kdc(context, realm, &servers, *use_master, no_udp);
493 if (context->kdc_send_hook != NULL) {
494 retval = context->kdc_send_hook(context, context->kdc_send_hook_data,
495 realm, message, &hook_message,
500 if (hook_reply != NULL) {
501 *reply_out = *hook_reply;
506 if (hook_message != NULL)
507 message = hook_message;
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;
518 k5_setmsg(context, retval,
519 _("Cannot contact any KDC for realm '%.*s'"),
520 realm->length, realm->data);
524 if (context->kdc_recv_hook != NULL) {
526 retval = context->kdc_recv_hook(context, context->kdc_recv_hook_data,
527 retval, realm, message, &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);
539 if (hook_reply != NULL) {
540 *reply_out = *hook_reply;
544 reply = empty_data();
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);
556 krb5_free_data(context, hook_message);
557 krb5_free_data_contents(context, &reply);
558 k5_free_serverlist(&servers);
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
571 * Always watch for responses from *any* of the servers. Eventually
572 * fix the UDP code to do the same.
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
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;
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)
593 krb5_kkdcp_message pm;
594 krb5_data *encoded_pm = NULL;
596 const char *uri_path;
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.
607 memset(&pm, 0, sizeof(pm));
608 ret = alloc_data(&pm.kerb_message, message->length + 4);
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);
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,
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) {
639 krb5_free_data_contents(NULL, &pm.kerb_message);
640 krb5_free_data(NULL, encoded_pm);
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)
650 struct outgoing_message *out = &state->out;
655 if (message == NULL || message->length == 0)
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);
664 } else if (state->addr.transport == HTTPS) {
665 ret = make_proxy_request(state, realm, message, &req, &reqlen);
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;
675 SG_SET(&out->sgbuf[0], message->data, message->length);
676 SG_SET(&out->sgbuf[1], NULL, 0);
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)
688 struct conn_state *state, **tailptr;
690 state = calloc(1, sizeof(*state));
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);
716 state->service_connect = NULL;
717 state->service_write = NULL;
718 state->service_read = service_udp_read;
720 if (*udpbufp == NULL) {
721 *udpbufp = malloc(MAX_DGRAM_SIZE);
725 state->in.buf = *udpbufp;
726 state->in.bufsize = MAX_DGRAM_SIZE;
729 /* Chain the new state onto the tail of the list. */
730 for (tailptr = conns; *tailptr != NULL; tailptr = &(*tailptr)->next);
737 translate_ai_error (int err)
746 /* All of these indicate bad inputs to getaddrinfo. */
749 /* Translate to standard errno code. */
752 /* Translate to standard errno code. */
754 #ifdef EAI_ADDRFAMILY
757 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
761 /* Name not known or no address data, but no error. Do
766 /* An argument buffer overflowed. */
767 return EINVAL; /* XXX */
771 /* System error, obviously. */
775 /* An error code we haven't handled? */
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
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)
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;
800 char portbuf[PORT_LENGTH];
802 /* Skip UDP entries if we don't want UDP. */
803 if (strategy == NO_UDP && entry->transport == UDP)
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);
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);
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;
833 result = snprintf(portbuf, sizeof(portbuf), "%d", entry->port);
834 if (SNPRINTF_OVERFLOW(result, sizeof(portbuf)))
836 TRACE_SENDTO_KDC_RESOLVING(context, entry->hostname);
837 err = getaddrinfo(entry->hostname, portbuf, &hint, &addrs);
839 return translate_ai_error(err);
841 /* Add each address with the specified or preferred transport. */
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,
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);
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)
871 static const int one = 1;
872 static const struct linger lopt = { 0, 0 };
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 */
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);
886 /* Start connecting to KDC. */
887 e = SOCKET_CONNECT(fd, (struct sockaddr *)&state->addr.saddr,
891 * This is the path that should be followed for non-blocking
894 if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
895 state->state = CONNECTING;
898 (void) closesocket(fd);
899 state->state = FAILED;
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.
908 state->state = WRITING;
913 * Here's where KPASSWD callback gets the socket information it needs for
918 e = callback_info->pfn_callback(state->fd, callback_info->data,
919 &state->callback_buffer);
921 (void) closesocket(fd);
922 state->fd = INVALID_SOCKET;
923 state->state = FAILED;
927 message = &state->callback_buffer;
930 e = set_transport_message(state, realm, message);
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;
939 if (state->addr.transport == UDP) {
942 sg_buf *sg = &state->out.sgbuf[0];
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,
949 (void) closesocket(state->fd);
950 state->fd = INVALID_SOCKET;
951 state->state = FAILED;
954 state->state = READING;
958 if (!cm_add_fd(selstate, state->fd)) {
959 (void) closesocket(state->fd);
960 state->fd = INVALID_SOCKET;
961 state->state = FAILED;
964 if (state->state == CONNECTING || state->state == WRITING)
965 cm_write(selstate, state->fd);
967 cm_read(selstate, state->fd);
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
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)
985 if (conn->state == INITIALIZING) {
986 return start_connection(context, conn, message, selstate,
987 realm, callback_info);
990 /* Did we already shut down this channel? */
991 if (conn->state == FAILED) {
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. */
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,
1008 /* Keep connection alive, we'll try again next pass.
1010 Is this likely to catch any errors we didn't get from the
1011 select callbacks? */
1014 /* Yay, it worked. */
1019 kill_conn(krb5_context context, struct conn_state *conn,
1020 struct select_state *selstate)
1022 free_http_tls_data(context, conn);
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);
1028 closesocket(conn->fd);
1029 conn->fd = INVALID_SOCKET;
1030 conn->state = FAILED;
1033 /* Check socket for error. */
1035 get_so_error(int fd)
1038 socklen_t sockerrlen;
1041 sockerrlen = sizeof(sockerr);
1042 e = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen);
1044 /* What to do now? */
1051 /* Perform next step in sending. Return true on usable data. */
1053 service_dispatch(krb5_context context, const krb5_data *realm,
1054 struct conn_state *conn, struct select_state *selstate,
1057 /* Check for a socket exception. */
1058 if (ssflags & SSF_EXCEPTION) {
1059 kill_conn(context, conn, selstate);
1063 switch (conn->state) {
1065 assert(conn->service_connect != NULL);
1066 return conn->service_connect(context, realm, conn, selstate);
1068 assert(conn->service_write != NULL);
1069 return conn->service_write(context, realm, conn, selstate);
1071 assert(conn->service_read != NULL);
1072 return conn->service_read(context, realm, conn, selstate);
1078 /* Initialize TCP transport. */
1080 service_tcp_connect(krb5_context context, const krb5_data *realm,
1081 struct conn_state *conn, struct select_state *selstate)
1083 /* Check whether the connection succeeded. */
1084 int e = get_so_error(conn->fd);
1087 TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(context, &conn->addr, e);
1088 kill_conn(context, conn, selstate);
1092 conn->state = WRITING;
1094 /* Record this connection's timeout for service_fds. */
1095 if (get_curtime_ms(&conn->endtime) == 0)
1096 conn->endtime += 10000;
1098 return conn->service_write(context, realm, conn, selstate);
1101 /* Sets conn->state to READING when done. */
1103 service_tcp_write(krb5_context context, const krb5_data *realm,
1104 struct conn_state *conn, struct select_state *selstate)
1107 SOCKET_WRITEV_TEMP tmp;
1109 TRACE_SENDTO_KDC_TCP_SEND(context, &conn->addr);
1110 nwritten = SOCKET_WRITEV(conn->fd, conn->out.sgp, conn->out.sg_count, tmp);
1112 TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, &conn->addr, SOCKET_ERRNO);
1113 kill_conn(context, conn, selstate);
1117 sg_buf *sgp = conn->out.sgp;
1118 if ((size_t)nwritten < SG_LEN(sgp)) {
1119 SG_ADVANCE(sgp, (size_t)nwritten);
1122 nwritten -= SG_LEN(sgp);
1124 conn->out.sg_count--;
1127 if (conn->out.sg_count == 0) {
1128 /* Done writing, switch to reading. */
1129 cm_read(selstate, conn->fd);
1130 conn->state = READING;
1135 /* Return true on usable data. */
1137 service_tcp_read(krb5_context context, const krb5_data *realm,
1138 struct conn_state *conn, struct select_state *selstate)
1142 struct incoming_message *in = &conn->in;
1144 if (in->bufsizebytes_read == 4) {
1146 nread = SOCKET_READ(conn->fd, &in->buf[in->pos], in->n_left);
1148 e = nread ? SOCKET_ERRNO : ECONNRESET;
1149 TRACE_SENDTO_KDC_TCP_ERROR_RECV(context, &conn->addr, e);
1150 kill_conn(context, conn, selstate);
1153 in->n_left -= nread;
1155 if (in->n_left <= 0)
1158 /* Reading length. */
1159 nread = SOCKET_READ(conn->fd, in->bufsizebytes + in->bufsizebytes_read,
1160 4 - in->bufsizebytes_read);
1162 e = nread ? SOCKET_ERRNO : ECONNRESET;
1163 TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, &conn->addr, e);
1164 kill_conn(context, conn, selstate);
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);
1175 in->bufsize = in->n_left = len;
1177 in->buf = malloc(len);
1178 if (in->buf == NULL) {
1179 kill_conn(context, conn, selstate);
1187 /* Process events on a UDP socket. Return true if we get a reply. */
1189 service_udp_read(krb5_context context, const krb5_data *realm,
1190 struct conn_state *conn, struct select_state *selstate)
1194 nread = recv(conn->fd, conn->in.buf, conn->in.bufsize, 0);
1196 TRACE_SENDTO_KDC_UDP_ERROR_RECV(context, &conn->addr, SOCKET_ERRNO);
1197 kill_conn(context, conn, selstate);
1200 conn->in.pos = nread;
1204 /* Set up conn->http.tls. Return true on success. */
1206 setup_tls(krb5_context context, const krb5_data *realm,
1207 struct conn_state *conn, struct select_state *selstate)
1209 krb5_error_code ret;
1210 krb5_boolean ok = FALSE;
1211 char **anchors = NULL, *realmstr = NULL;
1212 const char *names[4];
1214 if (init_tls_vtable(context) != 0 || context->tls->setup == NULL)
1217 realmstr = k5memdup0(realm->data, realm->length, &ret);
1218 if (realmstr == NULL)
1221 /* Load the configured anchors. */
1222 names[0] = KRB5_CONF_REALMS;
1223 names[1] = realmstr;
1224 names[2] = KRB5_CONF_HTTP_ANCHORS;
1226 ret = profile_get_values(context->profile, names, &anchors);
1227 if (ret != 0 && ret != PROF_NO_RELATION)
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);
1240 profile_free_list(anchors);
1244 /* Set conn->state to READING when done; otherwise, call a cm_set_. */
1246 service_https_write(krb5_context context, const krb5_data *realm,
1247 struct conn_state *conn, struct select_state *selstate)
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);
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));
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);
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. */
1279 https_read_bytes(krb5_context context, struct conn_state *conn,
1280 struct select_state *selstate)
1282 size_t bufsize, nread;
1285 struct incoming_message *in = &conn->in;
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);
1294 tmp = realloc(in->buf, bufsize);
1296 kill_conn(context, conn, selstate);
1300 in->bufsize = bufsize;
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)
1309 in->buf[in->pos] = '\0';
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);
1326 /* Return true on readable, valid KKDCPP data. */
1328 service_https_read(krb5_context context, const krb5_data *realm,
1329 struct conn_state *conn, struct select_state *selstate)
1331 krb5_kkdcp_message *pm = NULL;
1334 struct incoming_message *in = &conn->in;
1336 /* Read data through the encryption layer. */
1337 if (!https_read_bytes(context, conn, selstate))
1340 /* Find the beginning of the response body. */
1341 rep = strstr(in->buf, "\r\n\r\n");
1346 /* Decode the response. */
1347 buf = make_data((char *)rep, in->pos - (rep - in->buf));
1348 if (decode_krb5_kkdcp_message(&buf, &pm) != 0)
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) {
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);
1366 TRACE_SENDTO_KDC_HTTPS_ERROR(context, in->buf);
1367 k5_free_kkdcp_message(context, pm);
1368 kill_conn(context, conn, selstate);
1372 /* Return the maximum of endtime and the endtime fields of all currently active
1373 * TCP connections. */
1375 get_endtime(time_ms endtime, struct conn_state *conns)
1377 struct conn_state *state;
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;
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)
1396 struct conn_state *state;
1400 e = get_curtime_ms(&endtime);
1403 endtime += interval;
1406 while (selstate->nfds > 0) {
1407 e = cm_select_or_poll(selstate, get_endtime(endtime, conns),
1415 /* Timeout, return to caller. */
1418 /* Got something on a socket, process it. */
1419 for (state = conns; state != NULL; state = state->next) {
1422 if (state->fd == INVALID_SOCKET)
1424 ssflags = cm_get_ssflags(seltemp, state->fd);
1428 if (service_dispatch(context, realm, state, selstate, ssflags)) {
1431 if (msg_handler != NULL) {
1432 krb5_data reply = make_data(state->in.buf, state->in.pos);
1434 stop = (msg_handler(context, &reply, msg_handler_data) != 0);
1438 *winner_out = state;
1450 * Current worst-case timeout behavior:
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.
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.
1462 * Total = 2**(P+1) + U*P + T - 2.
1464 * If P=3, Total = 3*U + T + 14.
1465 * If P=4, Total = 4*U + T + 30.
1467 * Note that if you try to reach two ports on one server, it counts as two.
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.
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,
1481 /* return 0 -> keep going, 1 -> quit */
1482 int (*msg_handler)(krb5_context, const krb5_data *, void *),
1483 void *msg_handler_data)
1487 krb5_error_code retval;
1488 struct conn_state *conns = NULL, *state, **tailptr, *next, *winner;
1490 struct select_state *sel_state = NULL, *seltemp;
1491 char *udpbuf = NULL;
1492 krb5_boolean done = FALSE;
1494 *reply = empty_data();
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) {
1503 seltemp = &sel_state[1];
1504 cm_init_selstate(sel_state);
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,
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. */
1520 if (maybe_send(context, state, message, sel_state, realm,
1523 done = service_fds(context, sel_state, 1000, conns, seltemp,
1524 realm, msg_handler, msg_handler_data, &winner);
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) {
1533 if (maybe_send(context, state, message, sel_state, realm,
1536 done = service_fds(context, sel_state, 1000, conns, seltemp,
1537 realm, msg_handler, msg_handler_data, &winner);
1540 /* Wait for two seconds at the end of the first pass. */
1542 done = service_fds(context, sel_state, 2000, conns, seltemp,
1543 realm, msg_handler, msg_handler_data, &winner);
1546 /* Make remaining passes over all of the connections. */
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,
1553 done = service_fds(context, sel_state, 1000, conns, seltemp,
1554 realm, msg_handler, msg_handler_data, &winner);
1555 if (sel_state->nfds == 0)
1558 /* Wait for the delay backoff at the end of this pass. */
1560 done = service_fds(context, sel_state, delay, conns, seltemp,
1561 realm, msg_handler, msg_handler_data, &winner);
1563 if (sel_state->nfds == 0)
1568 if (sel_state->nfds == 0 || !done || winner == NULL) {
1569 retval = KRB5_KDC_UNREACH;
1573 *reply = make_data(winner->in.buf, winner->in.pos);
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);
1583 for (state = conns; state != NULL; 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);
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);
1600 if (reply->data != udpbuf)