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