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