2 * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "krb5_locl.h"
35 #include "send_to_kdc_plugin.h"
38 krb5_send_to_kdc_func func
;
43 * connect to a remote host and in the case of stream sockets, provide
44 * a timeout for the connexion.
48 timed_connect(int s
, struct addrinfo
*addr
, time_t tmout
)
50 struct timeval timeout
;
55 if (addr
->ai_socktype
!= SOCK_STREAM
)
56 return connect(s
, addr
->ai_addr
, addr
->ai_addrlen
);
58 flags
= fcntl(s
, F_GETFL
);
62 fcntl(s
, F_SETFL
, flags
| O_NONBLOCK
);
63 ret
= connect(s
, addr
->ai_addr
, addr
->ai_addrlen
);
64 if (ret
== -1 && errno
!= EINPROGRESS
)
70 timeout
.tv_sec
= tmout
;
73 ret
= select(s
+ 1, NULL
, &wfds
, NULL
, &timeout
);
74 if (ret
!= -1 || errno
!= EINTR
)
77 fcntl(s
, F_SETFL
, flags
);
86 * send the data in `req' on the socket `fd' (which is datagram iff udp)
87 * waiting `tmout' for a reply and returning the reply in `rep'.
88 * iff limit read up to this many bytes
89 * returns 0 and data in `rep' if succesful, otherwise -1
93 recv_loop (krb5_socket_t fd
,
100 struct timeval timeout
;
104 #ifndef NO_LIMIT_FD_SETSIZE
105 if (fd
>= FD_SETSIZE
) {
114 timeout
.tv_sec
= tmout
;
116 ret
= select (fd
+ 1, &fdset
, NULL
, NULL
, &timeout
);
121 } else if (ret
== 0) {
126 if (rk_SOCK_IOCTL (fd
, FIONREAD
, &nbytes
) < 0) {
127 krb5_data_free (rep
);
134 nbytes
= min((size_t)nbytes
, limit
- rep
->length
);
136 tmp
= realloc (rep
->data
, rep
->length
+ nbytes
);
138 krb5_data_free (rep
);
142 ret
= recv (fd
, (char*)tmp
+ rep
->length
, nbytes
, 0);
144 krb5_data_free (rep
);
149 } while(!udp
&& (limit
== 0 || rep
->length
< limit
));
154 * Send kerberos requests and receive a reply on a udp or any other kind
155 * of a datagram socket. See `recv_loop'.
159 send_and_recv_udp(krb5_socket_t fd
,
161 const krb5_data
*req
,
164 if (send (fd
, req
->data
, req
->length
, 0) < 0)
167 return recv_loop(fd
, tmout
, 1, 0, rep
);
171 * `send_and_recv' for a TCP (or any other stream) socket.
172 * Since there are no record limits on a stream socket the protocol here
173 * is to prepend the request with 4 bytes of its length and the reply
174 * is similarly encoded.
178 send_and_recv_tcp(krb5_socket_t fd
,
180 const krb5_data
*req
,
183 unsigned char len
[4];
184 unsigned long rep_len
;
187 _krb5_put_int(len
, req
->length
, 4);
188 if(net_write (fd
, len
, sizeof(len
)) < 0)
190 if(net_write (fd
, req
->data
, req
->length
) < 0)
192 if (recv_loop (fd
, tmout
, 0, 4, &len_data
) < 0)
194 if (len_data
.length
!= 4) {
195 krb5_data_free (&len_data
);
198 _krb5_get_int(len_data
.data
, &rep_len
, 4);
199 krb5_data_free (&len_data
);
200 if (recv_loop (fd
, tmout
, 0, rep_len
, rep
) < 0)
202 if(rep
->length
!= rep_len
) {
203 krb5_data_free (rep
);
210 _krb5_send_and_recv_tcp(krb5_socket_t fd
,
212 const krb5_data
*req
,
215 return send_and_recv_tcp(fd
, tmout
, req
, rep
);
219 * `send_and_recv' tailored for the HTTP protocol.
223 send_and_recv_http(krb5_socket_t fd
,
226 const krb5_data
*req
,
229 char *request
= NULL
;
232 int len
= base64_encode(req
->data
, req
->length
, &str
);
236 ret
= asprintf(&request
, "GET %s%s HTTP/1.0\r\n\r\n", prefix
, str
);
238 if (ret
< 0 || request
== NULL
)
240 ret
= net_write (fd
, request
, strlen(request
));
244 ret
= recv_loop(fd
, tmout
, 0, 0, rep
);
248 unsigned long rep_len
;
251 s
= realloc(rep
->data
, rep
->length
+ 1);
253 krb5_data_free (rep
);
257 p
= strstr(s
, "\r\n\r\n");
265 rep
->length
-= p
- s
;
266 if(rep
->length
< 4) { /* remove length */
272 _krb5_get_int(p
, &rep_len
, 4);
273 if (rep_len
!= rep
->length
) {
278 memmove(rep
->data
, p
+ 4, rep
->length
);
284 init_port(const char *s
, int fallback
)
289 sscanf (s
, "%d", &tmp
);
296 * Return 0 if succesful, otherwise 1
300 send_via_proxy (krb5_context context
,
301 const krb5_krbhst_info
*hi
,
302 const krb5_data
*send_data
,
305 char *proxy2
= strdup(context
->http_proxy
);
306 char *proxy
= proxy2
;
309 struct addrinfo hints
;
310 struct addrinfo
*ai
, *a
;
312 krb5_socket_t s
= rk_INVALID_SOCKET
;
313 char portstr
[NI_MAXSERV
];
317 if (strncmp (proxy
, "http://", 7) == 0)
320 colon
= strchr(proxy
, ':');
323 memset (&hints
, 0, sizeof(hints
));
324 hints
.ai_family
= PF_UNSPEC
;
325 hints
.ai_socktype
= SOCK_STREAM
;
326 snprintf (portstr
, sizeof(portstr
), "%d",
327 ntohs(init_port (colon
, htons(80))));
328 ret
= getaddrinfo (proxy
, portstr
, &hints
, &ai
);
331 return krb5_eai_to_heim_errno(ret
, errno
);
333 for (a
= ai
; a
!= NULL
; a
= a
->ai_next
) {
334 s
= socket (a
->ai_family
, a
->ai_socktype
| SOCK_CLOEXEC
, a
->ai_protocol
);
338 if (timed_connect (s
, a
, context
->kdc_timeout
) < 0) {
350 ret
= asprintf(&prefix
, "http://%s/", hi
->hostname
);
351 if(ret
< 0 || prefix
== NULL
) {
355 ret
= send_and_recv_http(s
, context
->kdc_timeout
,
356 prefix
, send_data
, receive
);
359 if(ret
== 0 && receive
->length
!= 0)
364 static krb5_error_code
365 send_via_plugin(krb5_context context
,
366 krb5_krbhst_info
*hi
,
368 const krb5_data
*send_data
,
371 struct krb5_plugin
*list
= NULL
, *e
;
374 ret
= _krb5_plugin_find(context
, PLUGIN_TYPE_DATA
, KRB5_PLUGIN_SEND_TO_KDC
, &list
);
375 if(ret
!= 0 || list
== NULL
)
376 return KRB5_PLUGIN_NO_HANDLE
;
378 for (e
= list
; e
!= NULL
; e
= _krb5_plugin_get_next(e
)) {
379 krb5plugin_send_to_kdc_ftable
*service
;
382 service
= _krb5_plugin_get_symbol(e
);
383 if (service
->minor_version
!= 0)
386 (*service
->init
)(context
, &ctx
);
387 ret
= (*service
->send_to_kdc
)(context
, ctx
, hi
,
388 timeout
, send_data
, receive
);
389 (*service
->fini
)(ctx
);
392 if (ret
!= KRB5_PLUGIN_NO_HANDLE
) {
393 krb5_set_error_message(context
, ret
,
394 N_("Plugin send_to_kdc failed to "
395 "lookup with error: %d", ""), ret
);
399 _krb5_plugin_free(list
);
400 return KRB5_PLUGIN_NO_HANDLE
;
405 * Send the data `send' to one host from `handle` and get back the reply
409 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
410 krb5_sendto (krb5_context context
,
411 const krb5_data
*send_data
,
412 krb5_krbhst_handle handle
,
419 krb5_data_zero(receive
);
421 for (i
= 0; i
< context
->max_retries
; ++i
) {
422 krb5_krbhst_info
*hi
;
424 while (krb5_krbhst_next(context
, handle
, &hi
) == 0) {
425 struct addrinfo
*ai
, *a
;
427 _krb5_debug(context
, 2,
428 "trying to communicate with host %s in realm %s",
429 hi
->hostname
, _krb5_krbhst_get_realm(handle
));
431 if (context
->send_to_kdc
) {
432 struct send_to_kdc
*s
= context
->send_to_kdc
;
434 ret
= (*s
->func
)(context
, s
->data
, hi
,
435 context
->kdc_timeout
, send_data
, receive
);
436 if (ret
== 0 && receive
->length
!= 0)
441 ret
= send_via_plugin(context
, hi
, context
->kdc_timeout
,
443 if (ret
== 0 && receive
->length
!= 0)
445 else if (ret
!= KRB5_PLUGIN_NO_HANDLE
)
448 if(hi
->proto
== KRB5_KRBHST_HTTP
&& context
->http_proxy
) {
449 if (send_via_proxy (context
, hi
, send_data
, receive
) == 0) {
456 ret
= krb5_krbhst_get_addrinfo(context
, hi
, &ai
);
460 for (a
= ai
; a
!= NULL
; a
= a
->ai_next
) {
461 fd
= socket (a
->ai_family
, a
->ai_socktype
| SOCK_CLOEXEC
, a
->ai_protocol
);
462 if (rk_IS_BAD_SOCKET(fd
))
465 if (timed_connect (fd
, a
, context
->kdc_timeout
) < 0) {
470 case KRB5_KRBHST_HTTP
:
471 ret
= send_and_recv_http(fd
, context
->kdc_timeout
,
472 "", send_data
, receive
);
474 case KRB5_KRBHST_TCP
:
475 ret
= send_and_recv_tcp (fd
, context
->kdc_timeout
,
478 case KRB5_KRBHST_UDP
:
479 ret
= send_and_recv_udp (fd
, context
->kdc_timeout
,
484 if(ret
== 0 && receive
->length
!= 0)
488 krb5_krbhst_reset(context
, handle
);
490 krb5_clear_error_message (context
);
491 ret
= KRB5_KDC_UNREACH
;
493 _krb5_debug(context
, 2,
494 "result of trying to talk to realm %s = %d",
495 _krb5_krbhst_get_realm(handle
), ret
);
499 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
500 krb5_sendto_kdc(krb5_context context
,
501 const krb5_data
*send_data
,
502 const krb5_realm
*realm
,
505 return krb5_sendto_kdc_flags(context
, send_data
, realm
, receive
, 0);
508 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
509 krb5_sendto_kdc_flags(krb5_context context
,
510 const krb5_data
*send_data
,
511 const krb5_realm
*realm
,
518 ret
= krb5_sendto_ctx_alloc(context
, &ctx
);
521 krb5_sendto_ctx_add_flags(ctx
, flags
);
522 krb5_sendto_ctx_set_func(ctx
, _krb5_kdc_retry
, NULL
);
524 ret
= krb5_sendto_context(context
, ctx
, send_data
, *realm
, receive
);
525 krb5_sendto_ctx_free(context
, ctx
);
529 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
530 krb5_set_send_to_kdc_func(krb5_context context
,
531 krb5_send_to_kdc_func func
,
534 free(context
->send_to_kdc
);
536 context
->send_to_kdc
= NULL
;
540 context
->send_to_kdc
= malloc(sizeof(*context
->send_to_kdc
));
541 if (context
->send_to_kdc
== NULL
) {
542 krb5_set_error_message(context
, ENOMEM
,
543 N_("malloc: out of memory", ""));
547 context
->send_to_kdc
->func
= func
;
548 context
->send_to_kdc
->data
= data
;
552 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
553 _krb5_copy_send_to_kdc_func(krb5_context context
, krb5_context to
)
555 if (context
->send_to_kdc
)
556 return krb5_set_send_to_kdc_func(to
,
557 context
->send_to_kdc
->func
,
558 context
->send_to_kdc
->data
);
560 return krb5_set_send_to_kdc_func(to
, NULL
, NULL
);
565 struct krb5_sendto_ctx_data
{
568 krb5_sendto_ctx_func func
;
572 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
573 krb5_sendto_ctx_alloc(krb5_context context
, krb5_sendto_ctx
*ctx
)
575 *ctx
= calloc(1, sizeof(**ctx
));
577 krb5_set_error_message(context
, ENOMEM
,
578 N_("malloc: out of memory", ""));
584 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
585 krb5_sendto_ctx_add_flags(krb5_sendto_ctx ctx
, int flags
)
590 KRB5_LIB_FUNCTION
int KRB5_LIB_CALL
591 krb5_sendto_ctx_get_flags(krb5_sendto_ctx ctx
)
596 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
597 krb5_sendto_ctx_set_type(krb5_sendto_ctx ctx
, int type
)
603 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
604 krb5_sendto_ctx_set_func(krb5_sendto_ctx ctx
,
605 krb5_sendto_ctx_func func
,
612 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
613 krb5_sendto_ctx_free(krb5_context context
, krb5_sendto_ctx ctx
)
615 memset(ctx
, 0, sizeof(*ctx
));
619 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
620 krb5_sendto_context(krb5_context context
,
622 const krb5_data
*send_data
,
623 const krb5_realm realm
,
627 krb5_krbhst_handle handle
= NULL
;
628 int type
, freectx
= 0;
631 krb5_data_zero(receive
);
635 ret
= krb5_sendto_ctx_alloc(context
, &ctx
);
642 if ((ctx
->flags
& KRB5_KRBHST_FLAGS_MASTER
) || context
->use_admin_kdc
)
643 type
= KRB5_KRBHST_ADMIN
;
645 type
= KRB5_KRBHST_KDC
;
648 if ((int)send_data
->length
> context
->large_msg_size
)
649 ctx
->flags
|= KRB5_KRBHST_FLAGS_LARGE_MSG
;
651 /* loop until we get back a appropriate response */
654 action
= KRB5_SENDTO_DONE
;
656 krb5_data_free(receive
);
658 if (handle
== NULL
) {
659 ret
= krb5_krbhst_init_flags(context
, realm
, type
,
660 ctx
->flags
, &handle
);
663 krb5_sendto_ctx_free(context
, ctx
);
668 ret
= krb5_sendto(context
, send_data
, handle
, receive
);
672 ret
= (*ctx
->func
)(context
, ctx
, ctx
->data
, receive
, &action
);
676 if (action
!= KRB5_SENDTO_CONTINUE
) {
677 krb5_krbhst_free(context
, handle
);
680 } while (action
!= KRB5_SENDTO_DONE
);
682 krb5_krbhst_free(context
, handle
);
683 if (ret
== KRB5_KDC_UNREACH
)
684 krb5_set_error_message(context
, ret
,
685 N_("unable to reach any KDC in realm %s", ""),
688 krb5_data_free(receive
);
690 krb5_sendto_ctx_free(context
, ctx
);
694 krb5_error_code KRB5_CALLCONV
695 _krb5_kdc_retry(krb5_context context
, krb5_sendto_ctx ctx
, void *data
,
696 const krb5_data
*reply
, int *action
)
701 if(krb5_rd_error(context
, reply
, &error
))
704 ret
= krb5_error_from_rd_error(context
, &error
, NULL
);
705 krb5_free_error_contents(context
, &error
);
708 case KRB5KRB_ERR_RESPONSE_TOO_BIG
: {
709 if (krb5_sendto_ctx_get_flags(ctx
) & KRB5_KRBHST_FLAGS_LARGE_MSG
)
711 krb5_sendto_ctx_add_flags(ctx
, KRB5_KRBHST_FLAGS_LARGE_MSG
);
712 *action
= KRB5_SENDTO_RESTART
;
715 case KRB5KDC_ERR_SVC_UNAVAILABLE
:
716 *action
= KRB5_SENDTO_CONTINUE
;