1b528cefcSMark Murray /* 2b528cefcSMark Murray * Copyright (c) 1997 - 2000 Kungliga Tekniska H�gskolan 3b528cefcSMark Murray * (Royal Institute of Technology, Stockholm, Sweden). 4b528cefcSMark Murray * All rights reserved. 5b528cefcSMark Murray * 6b528cefcSMark Murray * Redistribution and use in source and binary forms, with or without 7b528cefcSMark Murray * modification, are permitted provided that the following conditions 8b528cefcSMark Murray * are met: 9b528cefcSMark Murray * 10b528cefcSMark Murray * 1. Redistributions of source code must retain the above copyright 11b528cefcSMark Murray * notice, this list of conditions and the following disclaimer. 12b528cefcSMark Murray * 13b528cefcSMark Murray * 2. Redistributions in binary form must reproduce the above copyright 14b528cefcSMark Murray * notice, this list of conditions and the following disclaimer in the 15b528cefcSMark Murray * documentation and/or other materials provided with the distribution. 16b528cefcSMark Murray * 17b528cefcSMark Murray * 3. Neither the name of the Institute nor the names of its contributors 18b528cefcSMark Murray * may be used to endorse or promote products derived from this software 19b528cefcSMark Murray * without specific prior written permission. 20b528cefcSMark Murray * 21b528cefcSMark Murray * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22b528cefcSMark Murray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23b528cefcSMark Murray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24b528cefcSMark Murray * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25b528cefcSMark Murray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26b528cefcSMark Murray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27b528cefcSMark Murray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28b528cefcSMark Murray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29b528cefcSMark Murray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30b528cefcSMark Murray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31b528cefcSMark Murray * SUCH DAMAGE. 32b528cefcSMark Murray */ 33b528cefcSMark Murray 34b528cefcSMark Murray #include "krb5_locl.h" 35b528cefcSMark Murray 36b528cefcSMark Murray RCSID("$Id: send_to_kdc.c,v 1.36 2000/01/06 07:59:11 assar Exp $"); 37b528cefcSMark Murray 38b528cefcSMark Murray /* 39b528cefcSMark Murray * send the data in `req' on the socket `fd' (which is datagram iff udp) 40b528cefcSMark Murray * waiting `tmout' for a reply and returning the reply in `rep'. 41b528cefcSMark Murray * iff limit read up to this many bytes 42b528cefcSMark Murray * returns 0 and data in `rep' if succesful, otherwise -1 43b528cefcSMark Murray */ 44b528cefcSMark Murray 45b528cefcSMark Murray static int 46b528cefcSMark Murray recv_loop (int fd, 47b528cefcSMark Murray time_t tmout, 48b528cefcSMark Murray int udp, 49b528cefcSMark Murray size_t limit, 50b528cefcSMark Murray krb5_data *rep) 51b528cefcSMark Murray { 52b528cefcSMark Murray fd_set fdset; 53b528cefcSMark Murray struct timeval timeout; 54b528cefcSMark Murray int ret; 55b528cefcSMark Murray int nbytes; 56b528cefcSMark Murray 57b528cefcSMark Murray krb5_data_zero(rep); 58b528cefcSMark Murray do { 59b528cefcSMark Murray FD_ZERO(&fdset); 60b528cefcSMark Murray FD_SET(fd, &fdset); 61b528cefcSMark Murray timeout.tv_sec = tmout; 62b528cefcSMark Murray timeout.tv_usec = 0; 63b528cefcSMark Murray ret = select (fd + 1, &fdset, NULL, NULL, &timeout); 64b528cefcSMark Murray if (ret < 0) { 65b528cefcSMark Murray if (errno == EINTR) 66b528cefcSMark Murray continue; 67b528cefcSMark Murray return -1; 68b528cefcSMark Murray } else if (ret == 0) { 69b528cefcSMark Murray return 0; 70b528cefcSMark Murray } else { 71b528cefcSMark Murray void *tmp; 72b528cefcSMark Murray 73b528cefcSMark Murray if (ioctl (fd, FIONREAD, &nbytes) < 0) { 74b528cefcSMark Murray krb5_data_free (rep); 75b528cefcSMark Murray return -1; 76b528cefcSMark Murray } 77b528cefcSMark Murray if(nbytes == 0) 78b528cefcSMark Murray return 0; 79b528cefcSMark Murray 80b528cefcSMark Murray if (limit) 81b528cefcSMark Murray nbytes = min(nbytes, limit - rep->length); 82b528cefcSMark Murray 83b528cefcSMark Murray tmp = realloc (rep->data, rep->length + nbytes); 84b528cefcSMark Murray if (tmp == NULL) { 85b528cefcSMark Murray krb5_data_free (rep); 86b528cefcSMark Murray return -1; 87b528cefcSMark Murray } 88b528cefcSMark Murray rep->data = tmp; 89b528cefcSMark Murray ret = recv (fd, (char*)tmp + rep->length, nbytes, 0); 90b528cefcSMark Murray if (ret < 0) { 91b528cefcSMark Murray krb5_data_free (rep); 92b528cefcSMark Murray return -1; 93b528cefcSMark Murray } 94b528cefcSMark Murray rep->length += ret; 95b528cefcSMark Murray } 96b528cefcSMark Murray } while(!udp && (limit == 0 || rep->length < limit)); 97b528cefcSMark Murray return 0; 98b528cefcSMark Murray } 99b528cefcSMark Murray 100b528cefcSMark Murray /* 101b528cefcSMark Murray * Send kerberos requests and receive a reply on a udp or any other kind 102b528cefcSMark Murray * of a datagram socket. See `recv_loop'. 103b528cefcSMark Murray */ 104b528cefcSMark Murray 105b528cefcSMark Murray static int 106b528cefcSMark Murray send_and_recv_udp(int fd, 107b528cefcSMark Murray time_t tmout, 108b528cefcSMark Murray const krb5_data *req, 109b528cefcSMark Murray krb5_data *rep) 110b528cefcSMark Murray { 111b528cefcSMark Murray if (send (fd, req->data, req->length, 0) < 0) 112b528cefcSMark Murray return -1; 113b528cefcSMark Murray 114b528cefcSMark Murray return recv_loop(fd, tmout, 1, 0, rep); 115b528cefcSMark Murray } 116b528cefcSMark Murray 117b528cefcSMark Murray /* 118b528cefcSMark Murray * `send_and_recv' for a TCP (or any other stream) socket. 119b528cefcSMark Murray * Since there are no record limits on a stream socket the protocol here 120b528cefcSMark Murray * is to prepend the request with 4 bytes of its length and the reply 121b528cefcSMark Murray * is similarly encoded. 122b528cefcSMark Murray */ 123b528cefcSMark Murray 124b528cefcSMark Murray static int 125b528cefcSMark Murray send_and_recv_tcp(int fd, 126b528cefcSMark Murray time_t tmout, 127b528cefcSMark Murray const krb5_data *req, 128b528cefcSMark Murray krb5_data *rep) 129b528cefcSMark Murray { 130b528cefcSMark Murray unsigned char len[4]; 131b528cefcSMark Murray unsigned long rep_len; 132b528cefcSMark Murray krb5_data len_data; 133b528cefcSMark Murray 134b528cefcSMark Murray _krb5_put_int(len, req->length, 4); 135b528cefcSMark Murray if(net_write(fd, len, sizeof(len)) < 0) 136b528cefcSMark Murray return -1; 137b528cefcSMark Murray if(net_write(fd, req->data, req->length) < 0) 138b528cefcSMark Murray return -1; 139b528cefcSMark Murray if (recv_loop (fd, tmout, 0, 4, &len_data) < 0) 140b528cefcSMark Murray return -1; 141b528cefcSMark Murray if (len_data.length != 4) { 142b528cefcSMark Murray krb5_data_free (&len_data); 143b528cefcSMark Murray return -1; 144b528cefcSMark Murray } 145b528cefcSMark Murray _krb5_get_int(len_data.data, &rep_len, 4); 146b528cefcSMark Murray krb5_data_free (&len_data); 147b528cefcSMark Murray if (recv_loop (fd, tmout, 0, rep_len, rep) < 0) 148b528cefcSMark Murray return -1; 149b528cefcSMark Murray if(rep->length != rep_len) { 150b528cefcSMark Murray krb5_data_free (rep); 151b528cefcSMark Murray return -1; 152b528cefcSMark Murray } 153b528cefcSMark Murray return 0; 154b528cefcSMark Murray } 155b528cefcSMark Murray 156b528cefcSMark Murray /* 157b528cefcSMark Murray * `send_and_recv' tailored for the HTTP protocol. 158b528cefcSMark Murray */ 159b528cefcSMark Murray 160b528cefcSMark Murray static int 161b528cefcSMark Murray send_and_recv_http(int fd, 162b528cefcSMark Murray time_t tmout, 163b528cefcSMark Murray const char *prefix, 164b528cefcSMark Murray const krb5_data *req, 165b528cefcSMark Murray krb5_data *rep) 166b528cefcSMark Murray { 167b528cefcSMark Murray char *request; 168b528cefcSMark Murray char *str; 169b528cefcSMark Murray int ret; 170b528cefcSMark Murray int len = base64_encode(req->data, req->length, &str); 171b528cefcSMark Murray 172b528cefcSMark Murray if(len < 0) 173b528cefcSMark Murray return -1; 174b528cefcSMark Murray asprintf(&request, "GET %s%s HTTP/1.0\r\n\r\n", prefix, str); 175b528cefcSMark Murray free(str); 176b528cefcSMark Murray if (request == NULL) 177b528cefcSMark Murray return -1; 178b528cefcSMark Murray ret = net_write (fd, request, strlen(request)); 179b528cefcSMark Murray free (request); 180b528cefcSMark Murray if (ret < 0) 181b528cefcSMark Murray return ret; 182b528cefcSMark Murray ret = recv_loop(fd, tmout, 0, 0, rep); 183b528cefcSMark Murray if(ret) 184b528cefcSMark Murray return ret; 185b528cefcSMark Murray { 186b528cefcSMark Murray unsigned long rep_len; 187b528cefcSMark Murray char *s, *p; 188b528cefcSMark Murray 189b528cefcSMark Murray s = realloc(rep->data, rep->length + 1); 190b528cefcSMark Murray if (s == NULL) { 191b528cefcSMark Murray krb5_data_free (rep); 192b528cefcSMark Murray return -1; 193b528cefcSMark Murray } 194b528cefcSMark Murray s[rep->length] = 0; 195b528cefcSMark Murray p = strstr(s, "\r\n\r\n"); 196b528cefcSMark Murray if(p == NULL) { 197b528cefcSMark Murray free(s); 198b528cefcSMark Murray return -1; 199b528cefcSMark Murray } 200b528cefcSMark Murray p += 4; 201b528cefcSMark Murray rep->data = s; 202b528cefcSMark Murray rep->length -= p - s; 203b528cefcSMark Murray if(rep->length < 4) { /* remove length */ 204b528cefcSMark Murray free(s); 205b528cefcSMark Murray return -1; 206b528cefcSMark Murray } 207b528cefcSMark Murray rep->length -= 4; 208b528cefcSMark Murray _krb5_get_int(p, &rep_len, 4); 209b528cefcSMark Murray if (rep_len != rep->length) { 210b528cefcSMark Murray free(s); 211b528cefcSMark Murray return -1; 212b528cefcSMark Murray } 213b528cefcSMark Murray memmove(rep->data, p + 4, rep->length); 214b528cefcSMark Murray } 215b528cefcSMark Murray return 0; 216b528cefcSMark Murray } 217b528cefcSMark Murray 218b528cefcSMark Murray static int 219b528cefcSMark Murray init_port(const char *s, int fallback) 220b528cefcSMark Murray { 221b528cefcSMark Murray if (s) { 222b528cefcSMark Murray int tmp; 223b528cefcSMark Murray 224b528cefcSMark Murray sscanf (s, "%d", &tmp); 225b528cefcSMark Murray return htons(tmp); 226b528cefcSMark Murray } else 227b528cefcSMark Murray return fallback; 228b528cefcSMark Murray } 229b528cefcSMark Murray 230b528cefcSMark Murray /* 231b528cefcSMark Murray * Return 0 if succesful, otherwise 1 232b528cefcSMark Murray */ 233b528cefcSMark Murray 234b528cefcSMark Murray static int 235b528cefcSMark Murray send_via_proxy (krb5_context context, 236b528cefcSMark Murray const char *hostname, 237b528cefcSMark Murray const krb5_data *send, 238b528cefcSMark Murray krb5_data *receive) 239b528cefcSMark Murray { 240b528cefcSMark Murray char *proxy = strdup(context->http_proxy); 241b528cefcSMark Murray char *prefix; 242b528cefcSMark Murray char *colon; 243b528cefcSMark Murray struct addrinfo hints; 244b528cefcSMark Murray struct addrinfo *ai, *a; 245b528cefcSMark Murray int ret; 246b528cefcSMark Murray int s; 247b528cefcSMark Murray char portstr[NI_MAXSERV]; 248b528cefcSMark Murray 249b528cefcSMark Murray colon = strchr(proxy, ':'); 250b528cefcSMark Murray if(colon != NULL) 251b528cefcSMark Murray *colon++ = '\0'; 252b528cefcSMark Murray memset (&hints, 0, sizeof(hints)); 253b528cefcSMark Murray hints.ai_family = PF_UNSPEC; 254b528cefcSMark Murray hints.ai_socktype = SOCK_STREAM; 255b528cefcSMark Murray snprintf (portstr, sizeof(portstr), "%d", 256b528cefcSMark Murray ntohs(init_port (colon, htons(80)))); 257b528cefcSMark Murray ret = getaddrinfo (proxy, portstr, NULL, &ai); 258b528cefcSMark Murray free (proxy); 259b528cefcSMark Murray if (ret) 260b528cefcSMark Murray return ret; 261b528cefcSMark Murray 262b528cefcSMark Murray for (a = ai; a != NULL; a = a->ai_next) { 263b528cefcSMark Murray s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 264b528cefcSMark Murray if (s < 0) 265b528cefcSMark Murray continue; 266b528cefcSMark Murray if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 267b528cefcSMark Murray close (s); 268b528cefcSMark Murray continue; 269b528cefcSMark Murray } 270b528cefcSMark Murray break; 271b528cefcSMark Murray } 272b528cefcSMark Murray if (a == NULL) { 273b528cefcSMark Murray freeaddrinfo (ai); 274b528cefcSMark Murray return 1; 275b528cefcSMark Murray } 276b528cefcSMark Murray freeaddrinfo (ai); 277b528cefcSMark Murray 278b528cefcSMark Murray asprintf(&prefix, "http://%s/", hostname); 279b528cefcSMark Murray if(prefix == NULL) { 280b528cefcSMark Murray close(s); 281b528cefcSMark Murray return 1; 282b528cefcSMark Murray } 283b528cefcSMark Murray ret = send_and_recv_http(s, context->kdc_timeout, 284b528cefcSMark Murray prefix, send, receive); 285b528cefcSMark Murray close (s); 286b528cefcSMark Murray free(prefix); 287b528cefcSMark Murray if(ret == 0 && receive->length != 0) 288b528cefcSMark Murray return 0; 289b528cefcSMark Murray return 1; 290b528cefcSMark Murray } 291b528cefcSMark Murray 292b528cefcSMark Murray /* 293b528cefcSMark Murray * Send the data `send' to one KDC in `realm' and get back the reply 294b528cefcSMark Murray * in `receive'. 295b528cefcSMark Murray */ 296b528cefcSMark Murray 297b528cefcSMark Murray krb5_error_code 298b528cefcSMark Murray krb5_sendto_kdc (krb5_context context, 299b528cefcSMark Murray const krb5_data *send, 300b528cefcSMark Murray const krb5_realm *realm, 301b528cefcSMark Murray krb5_data *receive) 302b528cefcSMark Murray { 303b528cefcSMark Murray krb5_error_code ret; 304b528cefcSMark Murray char **hostlist, **hp, *p; 305b528cefcSMark Murray int fd; 306b528cefcSMark Murray int port; 307b528cefcSMark Murray int i; 308b528cefcSMark Murray 309b528cefcSMark Murray port = krb5_getportbyname (context, "kerberos", "udp", 88); 310b528cefcSMark Murray 311b528cefcSMark Murray if (context->use_admin_kdc) 312b528cefcSMark Murray ret = krb5_get_krb_admin_hst (context, realm, &hostlist); 313b528cefcSMark Murray else 314b528cefcSMark Murray ret = krb5_get_krbhst (context, realm, &hostlist); 315b528cefcSMark Murray if (ret) 316b528cefcSMark Murray return ret; 317b528cefcSMark Murray 318b528cefcSMark Murray for (i = 0; i < context->max_retries; ++i) 319b528cefcSMark Murray for (hp = hostlist; (p = *hp); ++hp) { 320b528cefcSMark Murray char *colon; 321b528cefcSMark Murray int http_flag = 0; 322b528cefcSMark Murray int tcp_flag = 0; 323b528cefcSMark Murray struct addrinfo *ai, *a; 324b528cefcSMark Murray struct addrinfo hints; 325b528cefcSMark Murray char portstr[NI_MAXSERV]; 326b528cefcSMark Murray 327b528cefcSMark Murray if(strncmp(p, "http://", 7) == 0){ 328b528cefcSMark Murray p += 7; 329b528cefcSMark Murray http_flag = 1; 330b528cefcSMark Murray port = htons(80); 331b528cefcSMark Murray } else if(strncmp(p, "http/", 5) == 0) { 332b528cefcSMark Murray p += 5; 333b528cefcSMark Murray http_flag = 1; 334b528cefcSMark Murray port = htons(80); 335b528cefcSMark Murray }else if(strncmp(p, "tcp/", 4) == 0){ 336b528cefcSMark Murray p += 4; 337b528cefcSMark Murray tcp_flag = 1; 338b528cefcSMark Murray } else if(strncmp(p, "udp/", 4) == 0) { 339b528cefcSMark Murray p += 4; 340b528cefcSMark Murray } 341b528cefcSMark Murray if(http_flag && context->http_proxy) { 342b528cefcSMark Murray if (send_via_proxy (context, p, send, receive)) 343b528cefcSMark Murray continue; 344b528cefcSMark Murray else 345b528cefcSMark Murray goto out; 346b528cefcSMark Murray } 347b528cefcSMark Murray colon = strchr (p, ':'); 348b528cefcSMark Murray if (colon) 349b528cefcSMark Murray *colon++ = '\0'; 350b528cefcSMark Murray 351b528cefcSMark Murray memset (&hints, 0, sizeof(hints)); 352b528cefcSMark Murray hints.ai_family = PF_UNSPEC; 353b528cefcSMark Murray if (tcp_flag || http_flag) 354b528cefcSMark Murray hints.ai_socktype = SOCK_STREAM; 355b528cefcSMark Murray else 356b528cefcSMark Murray hints.ai_socktype = SOCK_DGRAM; 357b528cefcSMark Murray snprintf (portstr, sizeof(portstr), "%d", 358b528cefcSMark Murray ntohs(init_port (colon, port))); 359b528cefcSMark Murray ret = getaddrinfo (p, portstr, &hints, &ai); 360b528cefcSMark Murray if (ret) 361b528cefcSMark Murray continue; 362b528cefcSMark Murray for (a = ai; a != NULL; a = a->ai_next) { 363b528cefcSMark Murray fd = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 364b528cefcSMark Murray if (fd < 0) 365b528cefcSMark Murray continue; 366b528cefcSMark Murray if (connect (fd, a->ai_addr, a->ai_addrlen) < 0) { 367b528cefcSMark Murray close (fd); 368b528cefcSMark Murray continue; 369b528cefcSMark Murray } 370b528cefcSMark Murray break; 371b528cefcSMark Murray } 372b528cefcSMark Murray if (a == NULL) { 373b528cefcSMark Murray freeaddrinfo (ai); 374b528cefcSMark Murray continue; 375b528cefcSMark Murray } 376b528cefcSMark Murray freeaddrinfo (ai); 377b528cefcSMark Murray 378b528cefcSMark Murray if(http_flag) 379b528cefcSMark Murray ret = send_and_recv_http(fd, context->kdc_timeout, 380b528cefcSMark Murray "", send, receive); 381b528cefcSMark Murray else if(tcp_flag) 382b528cefcSMark Murray ret = send_and_recv_tcp (fd, context->kdc_timeout, 383b528cefcSMark Murray send, receive); 384b528cefcSMark Murray else 385b528cefcSMark Murray ret = send_and_recv_udp (fd, context->kdc_timeout, 386b528cefcSMark Murray send, receive); 387b528cefcSMark Murray close (fd); 388b528cefcSMark Murray if(ret == 0 && receive->length != 0) 389b528cefcSMark Murray goto out; 390b528cefcSMark Murray } 391b528cefcSMark Murray ret = KRB5_KDC_UNREACH; 392b528cefcSMark Murray out: 393b528cefcSMark Murray krb5_free_krbhst (context, hostlist); 394b528cefcSMark Murray return ret; 395b528cefcSMark Murray } 396