send_to_kdc.c revision 55682
155682Smarkm/*
255682Smarkm * Copyright (c) 1997 - 2000 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "krb5_locl.h"
3555682Smarkm
3655682SmarkmRCSID("$Id: send_to_kdc.c,v 1.36 2000/01/06 07:59:11 assar Exp $");
3755682Smarkm
3855682Smarkm/*
3955682Smarkm * send the data in `req' on the socket `fd' (which is datagram iff udp)
4055682Smarkm * waiting `tmout' for a reply and returning the reply in `rep'.
4155682Smarkm * iff limit read up to this many bytes
4255682Smarkm * returns 0 and data in `rep' if succesful, otherwise -1
4355682Smarkm */
4455682Smarkm
4555682Smarkmstatic int
4655682Smarkmrecv_loop (int fd,
4755682Smarkm	   time_t tmout,
4855682Smarkm	   int udp,
4955682Smarkm	   size_t limit,
5055682Smarkm	   krb5_data *rep)
5155682Smarkm{
5255682Smarkm     fd_set fdset;
5355682Smarkm     struct timeval timeout;
5455682Smarkm     int ret;
5555682Smarkm     int nbytes;
5655682Smarkm
5755682Smarkm     krb5_data_zero(rep);
5855682Smarkm     do {
5955682Smarkm	 FD_ZERO(&fdset);
6055682Smarkm	 FD_SET(fd, &fdset);
6155682Smarkm	 timeout.tv_sec  = tmout;
6255682Smarkm	 timeout.tv_usec = 0;
6355682Smarkm	 ret = select (fd + 1, &fdset, NULL, NULL, &timeout);
6455682Smarkm	 if (ret < 0) {
6555682Smarkm	     if (errno == EINTR)
6655682Smarkm		 continue;
6755682Smarkm	     return -1;
6855682Smarkm	 } else if (ret == 0) {
6955682Smarkm	     return 0;
7055682Smarkm	 } else {
7155682Smarkm	     void *tmp;
7255682Smarkm
7355682Smarkm	     if (ioctl (fd, FIONREAD, &nbytes) < 0) {
7455682Smarkm		 krb5_data_free (rep);
7555682Smarkm		 return -1;
7655682Smarkm	     }
7755682Smarkm	     if(nbytes == 0)
7855682Smarkm		 return 0;
7955682Smarkm
8055682Smarkm	     if (limit)
8155682Smarkm		 nbytes = min(nbytes, limit - rep->length);
8255682Smarkm
8355682Smarkm	     tmp = realloc (rep->data, rep->length + nbytes);
8455682Smarkm	     if (tmp == NULL) {
8555682Smarkm		 krb5_data_free (rep);
8655682Smarkm		 return -1;
8755682Smarkm	     }
8855682Smarkm	     rep->data = tmp;
8955682Smarkm	     ret = recv (fd, (char*)tmp + rep->length, nbytes, 0);
9055682Smarkm	     if (ret < 0) {
9155682Smarkm		 krb5_data_free (rep);
9255682Smarkm		 return -1;
9355682Smarkm	     }
9455682Smarkm	     rep->length += ret;
9555682Smarkm	 }
9655682Smarkm     } while(!udp && (limit == 0 || rep->length < limit));
9755682Smarkm     return 0;
9855682Smarkm}
9955682Smarkm
10055682Smarkm/*
10155682Smarkm * Send kerberos requests and receive a reply on a udp or any other kind
10255682Smarkm * of a datagram socket.  See `recv_loop'.
10355682Smarkm */
10455682Smarkm
10555682Smarkmstatic int
10655682Smarkmsend_and_recv_udp(int fd,
10755682Smarkm		  time_t tmout,
10855682Smarkm		  const krb5_data *req,
10955682Smarkm		  krb5_data *rep)
11055682Smarkm{
11155682Smarkm    if (send (fd, req->data, req->length, 0) < 0)
11255682Smarkm	return -1;
11355682Smarkm
11455682Smarkm    return recv_loop(fd, tmout, 1, 0, rep);
11555682Smarkm}
11655682Smarkm
11755682Smarkm/*
11855682Smarkm * `send_and_recv' for a TCP (or any other stream) socket.
11955682Smarkm * Since there are no record limits on a stream socket the protocol here
12055682Smarkm * is to prepend the request with 4 bytes of its length and the reply
12155682Smarkm * is similarly encoded.
12255682Smarkm */
12355682Smarkm
12455682Smarkmstatic int
12555682Smarkmsend_and_recv_tcp(int fd,
12655682Smarkm		  time_t tmout,
12755682Smarkm		  const krb5_data *req,
12855682Smarkm		  krb5_data *rep)
12955682Smarkm{
13055682Smarkm    unsigned char len[4];
13155682Smarkm    unsigned long rep_len;
13255682Smarkm    krb5_data len_data;
13355682Smarkm
13455682Smarkm    _krb5_put_int(len, req->length, 4);
13555682Smarkm    if(net_write(fd, len, sizeof(len)) < 0)
13655682Smarkm	return -1;
13755682Smarkm    if(net_write(fd, req->data, req->length) < 0)
13855682Smarkm	return -1;
13955682Smarkm    if (recv_loop (fd, tmout, 0, 4, &len_data) < 0)
14055682Smarkm	return -1;
14155682Smarkm    if (len_data.length != 4) {
14255682Smarkm	krb5_data_free (&len_data);
14355682Smarkm	return -1;
14455682Smarkm    }
14555682Smarkm    _krb5_get_int(len_data.data, &rep_len, 4);
14655682Smarkm    krb5_data_free (&len_data);
14755682Smarkm    if (recv_loop (fd, tmout, 0, rep_len, rep) < 0)
14855682Smarkm	return -1;
14955682Smarkm    if(rep->length != rep_len) {
15055682Smarkm	krb5_data_free (rep);
15155682Smarkm	return -1;
15255682Smarkm    }
15355682Smarkm    return 0;
15455682Smarkm}
15555682Smarkm
15655682Smarkm/*
15755682Smarkm * `send_and_recv' tailored for the HTTP protocol.
15855682Smarkm */
15955682Smarkm
16055682Smarkmstatic int
16155682Smarkmsend_and_recv_http(int fd,
16255682Smarkm		   time_t tmout,
16355682Smarkm		   const char *prefix,
16455682Smarkm		   const krb5_data *req,
16555682Smarkm		   krb5_data *rep)
16655682Smarkm{
16755682Smarkm    char *request;
16855682Smarkm    char *str;
16955682Smarkm    int ret;
17055682Smarkm    int len = base64_encode(req->data, req->length, &str);
17155682Smarkm
17255682Smarkm    if(len < 0)
17355682Smarkm	return -1;
17455682Smarkm    asprintf(&request, "GET %s%s HTTP/1.0\r\n\r\n", prefix, str);
17555682Smarkm    free(str);
17655682Smarkm    if (request == NULL)
17755682Smarkm	return -1;
17855682Smarkm    ret = net_write (fd, request, strlen(request));
17955682Smarkm    free (request);
18055682Smarkm    if (ret < 0)
18155682Smarkm	return ret;
18255682Smarkm    ret = recv_loop(fd, tmout, 0, 0, rep);
18355682Smarkm    if(ret)
18455682Smarkm	return ret;
18555682Smarkm    {
18655682Smarkm	unsigned long rep_len;
18755682Smarkm	char *s, *p;
18855682Smarkm
18955682Smarkm	s = realloc(rep->data, rep->length + 1);
19055682Smarkm	if (s == NULL) {
19155682Smarkm	    krb5_data_free (rep);
19255682Smarkm	    return -1;
19355682Smarkm	}
19455682Smarkm	s[rep->length] = 0;
19555682Smarkm	p = strstr(s, "\r\n\r\n");
19655682Smarkm	if(p == NULL) {
19755682Smarkm	    free(s);
19855682Smarkm	    return -1;
19955682Smarkm	}
20055682Smarkm	p += 4;
20155682Smarkm	rep->data = s;
20255682Smarkm	rep->length -= p - s;
20355682Smarkm	if(rep->length < 4) { /* remove length */
20455682Smarkm	    free(s);
20555682Smarkm	    return -1;
20655682Smarkm	}
20755682Smarkm	rep->length -= 4;
20855682Smarkm	_krb5_get_int(p, &rep_len, 4);
20955682Smarkm	if (rep_len != rep->length) {
21055682Smarkm	    free(s);
21155682Smarkm	    return -1;
21255682Smarkm	}
21355682Smarkm	memmove(rep->data, p + 4, rep->length);
21455682Smarkm    }
21555682Smarkm    return 0;
21655682Smarkm}
21755682Smarkm
21855682Smarkmstatic int
21955682Smarkminit_port(const char *s, int fallback)
22055682Smarkm{
22155682Smarkm    if (s) {
22255682Smarkm	int tmp;
22355682Smarkm
22455682Smarkm	sscanf (s, "%d", &tmp);
22555682Smarkm	return htons(tmp);
22655682Smarkm    } else
22755682Smarkm	return fallback;
22855682Smarkm}
22955682Smarkm
23055682Smarkm/*
23155682Smarkm * Return 0 if succesful, otherwise 1
23255682Smarkm */
23355682Smarkm
23455682Smarkmstatic int
23555682Smarkmsend_via_proxy (krb5_context context,
23655682Smarkm		const char *hostname,
23755682Smarkm		const krb5_data *send,
23855682Smarkm		krb5_data *receive)
23955682Smarkm{
24055682Smarkm    char *proxy = strdup(context->http_proxy);
24155682Smarkm    char *prefix;
24255682Smarkm    char *colon;
24355682Smarkm    struct addrinfo hints;
24455682Smarkm    struct addrinfo *ai, *a;
24555682Smarkm    int ret;
24655682Smarkm    int s;
24755682Smarkm    char portstr[NI_MAXSERV];
24855682Smarkm
24955682Smarkm    colon = strchr(proxy, ':');
25055682Smarkm    if(colon != NULL)
25155682Smarkm	*colon++ = '\0';
25255682Smarkm    memset (&hints, 0, sizeof(hints));
25355682Smarkm    hints.ai_family   = PF_UNSPEC;
25455682Smarkm    hints.ai_socktype = SOCK_STREAM;
25555682Smarkm    snprintf (portstr, sizeof(portstr), "%d",
25655682Smarkm	      ntohs(init_port (colon, htons(80))));
25755682Smarkm    ret = getaddrinfo (proxy, portstr, NULL, &ai);
25855682Smarkm    free (proxy);
25955682Smarkm    if (ret)
26055682Smarkm	return ret;
26155682Smarkm
26255682Smarkm    for (a = ai; a != NULL; a = a->ai_next) {
26355682Smarkm	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
26455682Smarkm	if (s < 0)
26555682Smarkm	    continue;
26655682Smarkm	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
26755682Smarkm	    close (s);
26855682Smarkm	    continue;
26955682Smarkm	}
27055682Smarkm	break;
27155682Smarkm    }
27255682Smarkm    if (a == NULL) {
27355682Smarkm	freeaddrinfo (ai);
27455682Smarkm	return 1;
27555682Smarkm    }
27655682Smarkm    freeaddrinfo (ai);
27755682Smarkm
27855682Smarkm    asprintf(&prefix, "http://%s/", hostname);
27955682Smarkm    if(prefix == NULL) {
28055682Smarkm	close(s);
28155682Smarkm	return 1;
28255682Smarkm    }
28355682Smarkm    ret = send_and_recv_http(s, context->kdc_timeout,
28455682Smarkm			     prefix, send, receive);
28555682Smarkm    close (s);
28655682Smarkm    free(prefix);
28755682Smarkm    if(ret == 0 && receive->length != 0)
28855682Smarkm	return 0;
28955682Smarkm    return 1;
29055682Smarkm}
29155682Smarkm
29255682Smarkm/*
29355682Smarkm * Send the data `send' to one KDC in `realm' and get back the reply
29455682Smarkm * in `receive'.
29555682Smarkm */
29655682Smarkm
29755682Smarkmkrb5_error_code
29855682Smarkmkrb5_sendto_kdc (krb5_context context,
29955682Smarkm		 const krb5_data *send,
30055682Smarkm		 const krb5_realm *realm,
30155682Smarkm		 krb5_data *receive)
30255682Smarkm{
30355682Smarkm     krb5_error_code ret;
30455682Smarkm     char **hostlist, **hp, *p;
30555682Smarkm     int fd;
30655682Smarkm     int port;
30755682Smarkm     int i;
30855682Smarkm
30955682Smarkm     port = krb5_getportbyname (context, "kerberos", "udp", 88);
31055682Smarkm
31155682Smarkm     if (context->use_admin_kdc)
31255682Smarkm	 ret = krb5_get_krb_admin_hst (context, realm, &hostlist);
31355682Smarkm     else
31455682Smarkm	 ret = krb5_get_krbhst (context, realm, &hostlist);
31555682Smarkm     if (ret)
31655682Smarkm	  return ret;
31755682Smarkm
31855682Smarkm     for (i = 0; i < context->max_retries; ++i)
31955682Smarkm	 for (hp = hostlist; (p = *hp); ++hp) {
32055682Smarkm	     char *colon;
32155682Smarkm	     int http_flag = 0;
32255682Smarkm	     int tcp_flag = 0;
32355682Smarkm	     struct addrinfo *ai, *a;
32455682Smarkm	     struct addrinfo hints;
32555682Smarkm	     char portstr[NI_MAXSERV];
32655682Smarkm
32755682Smarkm	     if(strncmp(p, "http://", 7) == 0){
32855682Smarkm		 p += 7;
32955682Smarkm		 http_flag = 1;
33055682Smarkm		 port = htons(80);
33155682Smarkm	     } else if(strncmp(p, "http/", 5) == 0) {
33255682Smarkm		 p += 5;
33355682Smarkm		 http_flag = 1;
33455682Smarkm		 port = htons(80);
33555682Smarkm	     }else if(strncmp(p, "tcp/", 4) == 0){
33655682Smarkm		 p += 4;
33755682Smarkm		 tcp_flag = 1;
33855682Smarkm	     } else if(strncmp(p, "udp/", 4) == 0) {
33955682Smarkm		 p += 4;
34055682Smarkm	     }
34155682Smarkm	     if(http_flag && context->http_proxy) {
34255682Smarkm		 if (send_via_proxy (context, p, send, receive))
34355682Smarkm		     continue;
34455682Smarkm		 else
34555682Smarkm		     goto out;
34655682Smarkm	     }
34755682Smarkm	     colon = strchr (p, ':');
34855682Smarkm	     if (colon)
34955682Smarkm		 *colon++ = '\0';
35055682Smarkm
35155682Smarkm	     memset (&hints, 0, sizeof(hints));
35255682Smarkm	     hints.ai_family = PF_UNSPEC;
35355682Smarkm	     if (tcp_flag || http_flag)
35455682Smarkm		 hints.ai_socktype = SOCK_STREAM;
35555682Smarkm	     else
35655682Smarkm		 hints.ai_socktype = SOCK_DGRAM;
35755682Smarkm	     snprintf (portstr, sizeof(portstr), "%d",
35855682Smarkm		       ntohs(init_port (colon, port)));
35955682Smarkm	     ret = getaddrinfo (p, portstr, &hints, &ai);
36055682Smarkm	     if (ret)
36155682Smarkm		 continue;
36255682Smarkm	     for (a = ai; a != NULL; a = a->ai_next) {
36355682Smarkm		 fd = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
36455682Smarkm		 if (fd < 0)
36555682Smarkm		     continue;
36655682Smarkm		 if (connect (fd, a->ai_addr, a->ai_addrlen) < 0) {
36755682Smarkm		     close (fd);
36855682Smarkm		     continue;
36955682Smarkm		 }
37055682Smarkm		 break;
37155682Smarkm	     }
37255682Smarkm	     if (a == NULL) {
37355682Smarkm		 freeaddrinfo (ai);
37455682Smarkm		 continue;
37555682Smarkm	     }
37655682Smarkm	     freeaddrinfo (ai);
37755682Smarkm
37855682Smarkm	     if(http_flag)
37955682Smarkm		 ret = send_and_recv_http(fd, context->kdc_timeout,
38055682Smarkm					  "", send, receive);
38155682Smarkm	     else if(tcp_flag)
38255682Smarkm		 ret = send_and_recv_tcp (fd, context->kdc_timeout,
38355682Smarkm					  send, receive);
38455682Smarkm	     else
38555682Smarkm		 ret = send_and_recv_udp (fd, context->kdc_timeout,
38655682Smarkm					  send, receive);
38755682Smarkm	     close (fd);
38855682Smarkm	     if(ret == 0 && receive->length != 0)
38955682Smarkm		 goto out;
39055682Smarkm	 }
39155682Smarkm     ret = KRB5_KDC_UNREACH;
39255682Smarkmout:
39355682Smarkm     krb5_free_krbhst (context, hostlist);
39455682Smarkm     return ret;
39555682Smarkm}
396