1 /* 2 * Copyright (c) 1997 - 2002 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.48 2002/03/27 09:32:50 joda 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 if (fd >= FD_SETSIZE) { 58 return -1; 59 } 60 61 krb5_data_zero(rep); 62 do { 63 FD_ZERO(&fdset); 64 FD_SET(fd, &fdset); 65 timeout.tv_sec = tmout; 66 timeout.tv_usec = 0; 67 ret = select (fd + 1, &fdset, NULL, NULL, &timeout); 68 if (ret < 0) { 69 if (errno == EINTR) 70 continue; 71 return -1; 72 } else if (ret == 0) { 73 return 0; 74 } else { 75 void *tmp; 76 77 if (ioctl (fd, FIONREAD, &nbytes) < 0) { 78 krb5_data_free (rep); 79 return -1; 80 } 81 if(nbytes == 0) 82 return 0; 83 84 if (limit) 85 nbytes = min(nbytes, limit - rep->length); 86 87 tmp = realloc (rep->data, rep->length + nbytes); 88 if (tmp == NULL) { 89 krb5_data_free (rep); 90 return -1; 91 } 92 rep->data = tmp; 93 ret = recv (fd, (char*)tmp + rep->length, nbytes, 0); 94 if (ret < 0) { 95 krb5_data_free (rep); 96 return -1; 97 } 98 rep->length += ret; 99 } 100 } while(!udp && (limit == 0 || rep->length < limit)); 101 return 0; 102 } 103 104 /* 105 * Send kerberos requests and receive a reply on a udp or any other kind 106 * of a datagram socket. See `recv_loop'. 107 */ 108 109 static int 110 send_and_recv_udp(int fd, 111 time_t tmout, 112 const krb5_data *req, 113 krb5_data *rep) 114 { 115 if (send (fd, req->data, req->length, 0) < 0) 116 return -1; 117 118 return recv_loop(fd, tmout, 1, 0, rep); 119 } 120 121 /* 122 * `send_and_recv' for a TCP (or any other stream) socket. 123 * Since there are no record limits on a stream socket the protocol here 124 * is to prepend the request with 4 bytes of its length and the reply 125 * is similarly encoded. 126 */ 127 128 static int 129 send_and_recv_tcp(int fd, 130 time_t tmout, 131 const krb5_data *req, 132 krb5_data *rep) 133 { 134 unsigned char len[4]; 135 unsigned long rep_len; 136 krb5_data len_data; 137 138 _krb5_put_int(len, req->length, 4); 139 if(net_write(fd, len, sizeof(len)) < 0) 140 return -1; 141 if(net_write(fd, req->data, req->length) < 0) 142 return -1; 143 if (recv_loop (fd, tmout, 0, 4, &len_data) < 0) 144 return -1; 145 if (len_data.length != 4) { 146 krb5_data_free (&len_data); 147 return -1; 148 } 149 _krb5_get_int(len_data.data, &rep_len, 4); 150 krb5_data_free (&len_data); 151 if (recv_loop (fd, tmout, 0, rep_len, rep) < 0) 152 return -1; 153 if(rep->length != rep_len) { 154 krb5_data_free (rep); 155 return -1; 156 } 157 return 0; 158 } 159 160 /* 161 * `send_and_recv' tailored for the HTTP protocol. 162 */ 163 164 static int 165 send_and_recv_http(int fd, 166 time_t tmout, 167 const char *prefix, 168 const krb5_data *req, 169 krb5_data *rep) 170 { 171 char *request; 172 char *str; 173 int ret; 174 int len = base64_encode(req->data, req->length, &str); 175 176 if(len < 0) 177 return -1; 178 asprintf(&request, "GET %s%s HTTP/1.0\r\n\r\n", prefix, str); 179 free(str); 180 if (request == NULL) 181 return -1; 182 ret = net_write (fd, request, strlen(request)); 183 free (request); 184 if (ret < 0) 185 return ret; 186 ret = recv_loop(fd, tmout, 0, 0, rep); 187 if(ret) 188 return ret; 189 { 190 unsigned long rep_len; 191 char *s, *p; 192 193 s = realloc(rep->data, rep->length + 1); 194 if (s == NULL) { 195 krb5_data_free (rep); 196 return -1; 197 } 198 s[rep->length] = 0; 199 p = strstr(s, "\r\n\r\n"); 200 if(p == NULL) { 201 free(s); 202 return -1; 203 } 204 p += 4; 205 rep->data = s; 206 rep->length -= p - s; 207 if(rep->length < 4) { /* remove length */ 208 free(s); 209 return -1; 210 } 211 rep->length -= 4; 212 _krb5_get_int(p, &rep_len, 4); 213 if (rep_len != rep->length) { 214 free(s); 215 return -1; 216 } 217 memmove(rep->data, p + 4, rep->length); 218 } 219 return 0; 220 } 221 222 static int 223 init_port(const char *s, int fallback) 224 { 225 if (s) { 226 int tmp; 227 228 sscanf (s, "%d", &tmp); 229 return htons(tmp); 230 } else 231 return fallback; 232 } 233 234 /* 235 * Return 0 if succesful, otherwise 1 236 */ 237 238 static int 239 send_via_proxy (krb5_context context, 240 const krb5_krbhst_info *hi, 241 const krb5_data *send_data, 242 krb5_data *receive) 243 { 244 char *proxy2 = strdup(context->http_proxy); 245 char *proxy = proxy2; 246 char *prefix; 247 char *colon; 248 struct addrinfo hints; 249 struct addrinfo *ai, *a; 250 int ret; 251 int s = -1; 252 char portstr[NI_MAXSERV]; 253 254 if (proxy == NULL) 255 return ENOMEM; 256 if (strncmp (proxy, "http://", 7) == 0) 257 proxy += 7; 258 259 colon = strchr(proxy, ':'); 260 if(colon != NULL) 261 *colon++ = '\0'; 262 memset (&hints, 0, sizeof(hints)); 263 hints.ai_family = PF_UNSPEC; 264 hints.ai_socktype = SOCK_STREAM; 265 snprintf (portstr, sizeof(portstr), "%d", 266 ntohs(init_port (colon, htons(80)))); 267 ret = getaddrinfo (proxy, portstr, &hints, &ai); 268 free (proxy2); 269 if (ret) 270 return krb5_eai_to_heim_errno(ret, errno); 271 272 for (a = ai; a != NULL; a = a->ai_next) { 273 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 274 if (s < 0) 275 continue; 276 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 277 close (s); 278 continue; 279 } 280 break; 281 } 282 if (a == NULL) { 283 freeaddrinfo (ai); 284 return 1; 285 } 286 freeaddrinfo (ai); 287 288 asprintf(&prefix, "http://%s/", hi->hostname); 289 if(prefix == NULL) { 290 close(s); 291 return 1; 292 } 293 ret = send_and_recv_http(s, context->kdc_timeout, 294 prefix, send_data, receive); 295 close (s); 296 free(prefix); 297 if(ret == 0 && receive->length != 0) 298 return 0; 299 return 1; 300 } 301 302 /* 303 * Send the data `send' to one host from `handle` and get back the reply 304 * in `receive'. 305 */ 306 307 krb5_error_code 308 krb5_sendto (krb5_context context, 309 const krb5_data *send_data, 310 krb5_krbhst_handle handle, 311 krb5_data *receive) 312 { 313 krb5_error_code ret = 0; 314 int fd; 315 int i; 316 317 for (i = 0; i < context->max_retries; ++i) { 318 krb5_krbhst_info *hi; 319 320 while (krb5_krbhst_next(context, handle, &hi) == 0) { 321 int ret; 322 struct addrinfo *ai, *a; 323 324 if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) { 325 if (send_via_proxy (context, hi, send_data, receive)) 326 continue; 327 else 328 goto out; 329 } 330 331 ret = krb5_krbhst_get_addrinfo(context, hi, &ai); 332 if (ret) 333 continue; 334 335 for (a = ai; a != NULL; a = a->ai_next) { 336 fd = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 337 if (fd < 0) 338 continue; 339 if (connect (fd, a->ai_addr, a->ai_addrlen) < 0) { 340 close (fd); 341 continue; 342 } 343 switch (hi->proto) { 344 case KRB5_KRBHST_HTTP : 345 ret = send_and_recv_http(fd, context->kdc_timeout, 346 "", send_data, receive); 347 break; 348 case KRB5_KRBHST_TCP : 349 ret = send_and_recv_tcp (fd, context->kdc_timeout, 350 send_data, receive); 351 break; 352 case KRB5_KRBHST_UDP : 353 ret = send_and_recv_udp (fd, context->kdc_timeout, 354 send_data, receive); 355 break; 356 } 357 close (fd); 358 if(ret == 0 && receive->length != 0) 359 goto out; 360 } 361 } 362 krb5_krbhst_reset(context, handle); 363 } 364 krb5_clear_error_string (context); 365 ret = KRB5_KDC_UNREACH; 366 out: 367 return ret; 368 } 369 370 krb5_error_code 371 krb5_sendto_kdc2(krb5_context context, 372 const krb5_data *send_data, 373 const krb5_realm *realm, 374 krb5_data *receive, 375 krb5_boolean master) 376 { 377 krb5_error_code ret; 378 krb5_krbhst_handle handle; 379 int type; 380 381 if (master || context->use_admin_kdc) 382 type = KRB5_KRBHST_ADMIN; 383 else 384 type = KRB5_KRBHST_KDC; 385 386 ret = krb5_krbhst_init(context, *realm, type, &handle); 387 if (ret) 388 return ret; 389 390 ret = krb5_sendto(context, send_data, handle, receive); 391 krb5_krbhst_free(context, handle); 392 if (ret == KRB5_KDC_UNREACH) 393 krb5_set_error_string(context, 394 "unable to reach any KDC in realm %s", *realm); 395 return ret; 396 } 397 398 krb5_error_code 399 krb5_sendto_kdc(krb5_context context, 400 const krb5_data *send_data, 401 const krb5_realm *realm, 402 krb5_data *receive) 403 { 404 return krb5_sendto_kdc2(context, send_data, realm, receive, FALSE); 405 } 406