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