2 Copyright (C) 1989 by the Massachusetts Institute of Technology
4 Export of this software from the United States of America is assumed
5 to require a specific license from the United States Government.
6 It is the responsibility of any person or organization contemplating
7 export to obtain such a license before exporting.
9 WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10 distribute this software and its documentation for any purpose and
11 without fee is hereby granted, provided that the above copyright
12 notice appear in all copies and that both that copyright notice and
13 this permission notice appear in supporting documentation, and that
14 the name of M.I.T. not be used in advertising or publicity pertaining
15 to distribution of the software without specific, written prior
16 permission. M.I.T. makes no representations about the suitability of
17 this software for any purpose. It is provided "as is" without express
25 RCSID("$Id: send_to_kdc.c,v 1.71.2.1 2000/10/10 12:47:21 assar Exp $");
28 struct sockaddr_in addr;
30 enum krb_host_proto proto;
33 static int send_recv(KTEXT pkt, KTEXT rpkt, struct host *host);
36 * send_to_kdc() sends a message to the Kerberos authentication
37 * server(s) in the given realm and returns the reply message.
38 * The "pkt" argument points to the message to be sent to Kerberos;
39 * the "rpkt" argument will be filled in with Kerberos' reply.
40 * The "realm" argument indicates the realm of the Kerberos server(s)
41 * to transact with. If the realm is null, the local realm is used.
43 * If more than one Kerberos server is known for a given realm,
44 * different servers will be queried until one of them replies.
45 * Several attempts (retries) are made for each server before
48 * If an answer was received from a Kerberos host, KSUCCESS is
49 * returned. The following errors can be returned:
51 * SKDC_CANT - can't get local realm
52 * - can't find "kerberos" in /etc/services database
56 * - couldn't find any Kerberos host
58 * SKDC_RETRY - couldn't get an answer from any Kerberos server,
59 * after several retries
62 /* always use the admin server */
63 static int krb_use_admin_server_flag = 0;
65 static int client_timeout = -1;
68 krb_use_admin_server(int flag)
70 int old = krb_use_admin_server_flag;
71 krb_use_admin_server_flag = flag;
75 #define PROXY_VAR "krb4_proxy"
78 expand (struct host **ptr, size_t sz)
82 tmp = realloc (*ptr, sz) ;
90 send_to_kdc(KTEXT pkt, KTEXT rpkt, const char *realm)
93 int no_host; /* was a kerberos host found? */
98 char lrealm[REALM_SZ];
99 struct krb_host *k_host;
100 struct host *hosts = malloc(sizeof(*hosts));
101 const char *proxy = krb_get_config_string (PROXY_VAR);
106 if (client_timeout == -1) {
109 client_timeout = CLIENT_KRB_TIMEOUT;
110 to = krb_get_config_string ("kdc_timeout");
115 tmp = strtol (to, &end, 0);
117 client_timeout = tmp;
122 * If "realm" is non-null, use that, otherwise get the
126 if (krb_get_lrealm(lrealm,1)) {
128 krb_warning("send_to_kdc: can't get local realm\n");
134 krb_warning("lrealm is %s\n", realm);
137 /* get an initial allocation */
140 (k_host = krb_get_host(i, realm, krb_use_admin_server_flag));
148 if (k_host->proto == PROTO_HTTP && proxy != NULL) {
152 retval = expand (&hosts, (n_hosts + n_addrs) * sizeof(*hosts));
156 memset (&hosts[n_hosts].addr, 0, sizeof(struct sockaddr_in));
157 hosts[n_hosts].addr.sin_port = htons(k_host->port);
158 hosts[n_hosts].proto = k_host->proto;
159 hosts[n_hosts].hostname = k_host->host;
162 krb_warning("Getting host entry for %s...", k_host->host);
163 host = gethostbyname(k_host->host);
166 host ? "Got it" : "Didn't get it");
170 no_host = 0; /* found at least one */
173 for (addr_list = host->h_addr_list;
178 retval = expand (&hosts, (n_hosts + n_addrs) * sizeof(*hosts));
182 for (addr_list = host->h_addr_list, j = 0;
183 (p = *addr_list) != NULL;
185 memset (&hosts[n_hosts + j].addr, 0,
186 sizeof(struct sockaddr_in));
187 hosts[n_hosts + j].addr.sin_family = host->h_addrtype;
188 hosts[n_hosts + j].addr.sin_port = htons(k_host->port);
189 hosts[n_hosts + j].proto = k_host->proto;
190 hosts[n_hosts + j].hostname = k_host->host;
191 memcpy(&hosts[n_hosts + j].addr.sin_addr, p,
192 sizeof(struct in_addr));
196 for (j = 0; j < n_addrs; ++j) {
197 if (send_recv(pkt, rpkt, &hosts[n_hosts + j])) {
202 krb_warning("Timeout, error, or wrong descriptor\n");
209 krb_warning("send_to_kdc: can't find any Kerberos host.\n");
213 /* retry each host in sequence */
214 for (retry = 0; retry < CLIENT_KRB_RETRY; ++retry) {
215 for (i = 0; i < n_hosts; ++i) {
216 if (send_recv(pkt, rpkt, &hosts[i])) {
231 return socket(AF_INET, SOCK_DGRAM, 0);
235 udp_connect(int s, struct host *host)
238 krb_warning("connecting to %s (%s) udp, port %d\n",
240 inet_ntoa(host->addr.sin_addr),
241 ntohs(host->addr.sin_port));
243 return connect(s, (struct sockaddr*)&host->addr, sizeof(host->addr));
247 udp_send(int s, struct host *host, KTEXT pkt)
250 krb_warning("sending %d bytes to %s (%s), udp port %d\n",
253 inet_ntoa(host->addr.sin_addr),
254 ntohs(host->addr.sin_port));
256 return send(s, pkt->dat, pkt->length, 0);
262 return socket(AF_INET, SOCK_STREAM, 0);
266 tcp_connect(int s, struct host *host)
269 krb_warning("connecting to %s (%s), tcp port %d\n",
271 inet_ntoa(host->addr.sin_addr),
272 ntohs(host->addr.sin_port));
274 return connect(s, (struct sockaddr*)&host->addr, sizeof(host->addr));
278 tcp_send(int s, struct host *host, KTEXT pkt)
280 unsigned char len[4];
283 krb_warning("sending %d bytes to %s (%s), tcp port %d\n",
286 inet_ntoa(host->addr.sin_addr),
287 ntohs(host->addr.sin_port));
289 krb_put_int(pkt->length, len, sizeof(len), 4);
290 if(send(s, len, sizeof(len), 0) != sizeof(len))
292 return send(s, pkt->dat, pkt->length, 0);
296 udptcp_recv(void *buf, size_t len, KTEXT rpkt)
298 int pktlen = min(len, MAX_KTXT_LEN);
301 krb_warning("recieved %lu bytes on udp/tcp socket\n",
303 memcpy(rpkt->dat, buf, pktlen);
304 rpkt->length = pktlen;
309 url_parse(const char *url, char *host, size_t len, short *port)
314 if(strncmp(url, "http://", 7))
317 p = strchr(url, ':');
321 *port = htons(strtol(p + 1, &end, 0));
326 *port = k_getportbyname ("http", "tcp", htons(80));
327 p = strchr(url, '/');
335 memcpy(host, url, n);
341 http_connect(int s, struct host *host)
343 const char *proxy = krb_get_config_string(PROXY_VAR);
344 char proxy_host[MaxHostNameLen];
347 struct sockaddr_in sin;
351 krb_warning("Not using proxy.\n");
352 return tcp_connect(s, host);
354 if(url_parse(proxy, proxy_host, sizeof(proxy_host), &port) < 0)
356 hp = gethostbyname(proxy_host);
359 memset(&sin, 0, sizeof(sin));
360 sin.sin_family = AF_INET;
361 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
364 krb_warning("connecting to proxy on %s (%s) port %d\n",
365 proxy_host, inet_ntoa(sin.sin_addr), ntohs(port));
367 return connect(s, (struct sockaddr*)&sin, sizeof(sin));
371 http_send(int s, struct host *host, KTEXT pkt)
373 const char *proxy = krb_get_config_string (PROXY_VAR);
377 if(base64_encode(pkt->dat, pkt->length, &str) < 0)
381 krb_warning("sending %d bytes to %s, tcp port %d (via proxy)\n",
384 ntohs(host->addr.sin_port));
386 asprintf(&msg, "GET http://%s:%d/%s HTTP/1.0\r\n\r\n",
388 ntohs(host->addr.sin_port),
392 krb_warning("sending %d bytes to %s (%s), http port %d\n",
395 inet_ntoa(host->addr.sin_addr),
396 ntohs(host->addr.sin_port));
398 asprintf(&msg, "GET %s HTTP/1.0\r\n\r\n", str);
405 if(send(s, msg, strlen(msg), 0) != strlen(msg)){
414 http_recv(void *buf, size_t len, KTEXT rpkt)
417 char *tmp = malloc(len + 1);
421 memcpy(tmp, buf, len);
423 p = strstr(tmp, "\r\n\r\n");
430 krb_warning("recieved %lu bytes on http socket\n",
431 (unsigned long)((tmp + len) - p));
432 if((tmp + len) - p > MAX_KTXT_LEN) {
436 if (strncasecmp (tmp, "HTTP/1.0 2", 10) != 0
437 && strncasecmp (tmp, "HTTP/1.1 2", 10) != 0) {
441 memcpy(rpkt->dat, p, (tmp + len) - p);
442 rpkt->length = (tmp + len) - p;
447 static struct proto_descr {
451 int (*connect)(int, struct host *host);
452 int (*send)(int, struct host *host, KTEXT);
453 int (*recv)(void*, size_t, KTEXT);
455 { PROTO_UDP, 0, udp_socket, udp_connect, udp_send, udptcp_recv },
456 { PROTO_TCP, 1, tcp_socket, tcp_connect, tcp_send, udptcp_recv },
457 { PROTO_HTTP, 1, tcp_socket, http_connect, http_send, http_recv }
461 send_recv(KTEXT pkt, KTEXT rpkt, struct host *host)
465 unsigned char buf[MAX_KTXT_LEN];
468 for(i = 0; i < sizeof(protos) / sizeof(protos[0]); i++){
469 if(protos[i].proto == host->proto)
472 if(i == sizeof(protos) / sizeof(protos[0]))
474 if((s = (*protos[i].socket)()) < 0)
476 if((*protos[i].connect)(s, host) < 0) {
480 if((*protos[i].send)(s, host, pkt) < 0) {
486 struct timeval timeout;
488 timeout.tv_sec = client_timeout;
491 if (s >= FD_SETSIZE) {
493 krb_warning("fd too large\n");
499 /* select - either recv is ready, or timeout */
500 /* see if timeout or error or wrong descriptor */
501 if(select(s + 1, &readfds, 0, 0, &timeout) < 1
502 || !FD_ISSET(s, &readfds)) {
504 krb_warning("select failed: errno = %d\n", errno);
508 len = recv(s, buf + offset, sizeof(buf) - offset, 0);
516 } while(protos[i].stream_flag);
518 if((*protos[i].recv)(buf, offset, rpkt) < 0)
523 /* The configuration line "hosts: dns files" in /etc/nsswitch.conf is
524 * rumored to avoid triggering this bug. */
525 #if defined(linux) && defined(HAVE__DNS_GETHOSTBYNAME) && 0
526 /* Linux libc 5.3 is broken probably somewhere in nsw_hosts.o,
527 * for now keep this kludge. */
529 struct hostent *gethostbyname(const char *name)
531 return (void *)_dns_gethostbyname(name);