diff options
Diffstat (limited to 'crypto/kerberosIV/lib/krb/send_to_kdc.c')
-rw-r--r-- | crypto/kerberosIV/lib/krb/send_to_kdc.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/crypto/kerberosIV/lib/krb/send_to_kdc.c b/crypto/kerberosIV/lib/krb/send_to_kdc.c new file mode 100644 index 0000000..828b34d --- /dev/null +++ b/crypto/kerberosIV/lib/krb/send_to_kdc.c @@ -0,0 +1,251 @@ +/* + Copyright (C) 1989 by the Massachusetts Institute of Technology + + Export of this software from the United States of America is assumed + to require a specific license from the United States Government. + It is the responsibility of any person or organization contemplating + export to obtain such a license before exporting. + +WITHIN THAT CONSTRAINT, permission to use, copy, modify, and +distribute this software and its documentation for any purpose and +without fee is hereby granted, provided that the above copyright +notice appear in all copies and that both that copyright notice and +this permission notice appear in supporting documentation, and that +the name of M.I.T. not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. M.I.T. makes no representations about the suitability of +this software for any purpose. It is provided "as is" without express +or implied warranty. + + */ + +#include "krb_locl.h" + +RCSID("$Id: send_to_kdc.c,v 1.39 1997/05/15 21:02:31 joda Exp $"); + +struct host { + struct sockaddr_in addr; + int proto; +}; + +static const char *prog = "send_to_kdc"; +static send_recv(KTEXT pkt, KTEXT rpkt, int f, + struct sockaddr_in *_to, struct host *addrs, + int h_hosts); + +/* + * This file contains two routines, send_to_kdc() and send_recv(). + * send_recv() is a static routine used by send_to_kdc(). + */ + +/* + * send_to_kdc() sends a message to the Kerberos authentication + * server(s) in the given realm and returns the reply message. + * The "pkt" argument points to the message to be sent to Kerberos; + * the "rpkt" argument will be filled in with Kerberos' reply. + * The "realm" argument indicates the realm of the Kerberos server(s) + * to transact with. If the realm is null, the local realm is used. + * + * If more than one Kerberos server is known for a given realm, + * different servers will be queried until one of them replies. + * Several attempts (retries) are made for each server before + * giving up entirely. + * + * If an answer was received from a Kerberos host, KSUCCESS is + * returned. The following errors can be returned: + * + * SKDC_CANT - can't get local realm + * - can't find "kerberos" in /etc/services database + * - can't open socket + * - can't bind socket + * - all ports in use + * - couldn't find any Kerberos host + * + * SKDC_RETRY - couldn't get an answer from any Kerberos server, + * after several retries + */ + +int +send_to_kdc(KTEXT pkt, KTEXT rpkt, char *realm) +{ + int i; + int no_host; /* was a kerberos host found? */ + int retry; + int n_hosts; + int retval; + struct hostent *host; + char lrealm[REALM_SZ]; + struct krb_host *k_host; + struct host *hosts = malloc(sizeof(*hosts)); + + if (hosts == NULL) + return SKDC_CANT; + + /* + * If "realm" is non-null, use that, otherwise get the + * local realm. + */ + if (realm) + strcpy(lrealm, realm); + else + if (krb_get_lrealm(lrealm,1)) { + if (krb_debug) + krb_warning("%s: can't get local realm\n", prog); + return(SKDC_CANT); + } + if (krb_debug) + krb_warning("lrealm is %s\n", lrealm); + + no_host = 1; + /* get an initial allocation */ + n_hosts = 0; + for (i = 1; (k_host = krb_get_host(i, lrealm, 0)); ++i) { + char *p; + + if (krb_debug) + krb_warning("Getting host entry for %s...", k_host->host); + host = gethostbyname(k_host->host); + if (krb_debug) { + krb_warning("%s.\n", + host ? "Got it" : "Didn't get it"); + } + if (!host) + continue; + no_host = 0; /* found at least one */ + while ((p = *(host->h_addr_list)++)) { + hosts = realloc(hosts, sizeof(*hosts) * (n_hosts + 1)); + if (hosts == NULL) + return SKDC_CANT; + memset (&hosts[n_hosts].addr, 0, sizeof(hosts[n_hosts].addr)); + hosts[n_hosts].addr.sin_family = host->h_addrtype; + hosts[n_hosts].addr.sin_port = htons(k_host->port); + hosts[n_hosts].proto = k_host->proto; + memcpy(&hosts[n_hosts].addr.sin_addr, p, + sizeof(hosts[n_hosts].addr.sin_addr)); + ++n_hosts; + if (send_recv(pkt, rpkt, hosts[n_hosts-1].proto, + &hosts[n_hosts-1].addr, hosts, n_hosts)) { + retval = KSUCCESS; + goto rtn; + } + if (krb_debug) { + krb_warning("Timeout, error, or wrong descriptor\n"); + } + } + } + if (no_host) { + if (krb_debug) + krb_warning("%s: can't find any Kerberos host.\n", + prog); + retval = SKDC_CANT; + goto rtn; + } + /* retry each host in sequence */ + for (retry = 0; retry < CLIENT_KRB_RETRY; ++retry) { + for (i = 0; i < n_hosts; ++i) { + if (send_recv(pkt, rpkt, + hosts[i].proto, + &hosts[i].addr, + hosts, + n_hosts)) { + retval = KSUCCESS; + goto rtn; + } + } + } + retval = SKDC_RETRY; +rtn: + free(hosts); + return(retval); +} + +/* + * try to send out and receive message. + * return 1 on success, 0 on failure + */ + +static int +send_recv_it(KTEXT pkt, KTEXT rpkt, int stream, int f, + struct sockaddr_in *_to, struct host *addrs, int n_hosts) +{ + fd_set readfds; + int numsent; + + /* CLIENT_KRB_TIMEOUT indicates the time to wait before + * retrying a server. It's defined in "krb.h". + */ + struct timeval timeout; + timeout.tv_sec = CLIENT_KRB_TIMEOUT; + timeout.tv_usec = 0; + + if (krb_debug) { + if (_to->sin_family == AF_INET) + krb_warning("Sending message to %s...", + inet_ntoa(_to->sin_addr)); + else + krb_warning("Sending message..."); + } + if(stream){ + unsigned char tmp[4]; + krb_put_int(pkt->length, tmp, 4); + if((numsent = send(f, tmp, 4, 0)) != 4){ + if (krb_debug) + krb_warning("sent only %d/%d\n", numsent, 4); + return 0; + } + } + if ((numsent = send(f, pkt->dat, pkt->length, 0)) != pkt->length) { + if (krb_debug) + krb_warning("sent only %d/%d\n",numsent, pkt->length); + return 0; + } + if (krb_debug) + krb_warning("Sent\nWaiting for reply..."); + FD_ZERO(&readfds); + FD_SET(f, &readfds); + /* select - either recv is ready, or timeout */ + /* see if timeout or error or wrong descriptor */ + if (select(f + 1, &readfds, 0, 0, &timeout) < 1 + || !FD_ISSET(f, &readfds)) { + if (krb_debug) + krb_warning("select failed: errno = %d", errno); + return 0; + } + if(stream){ + if(krb_net_read(f, rpkt->dat, sizeof(rpkt->dat)) <= 0) + return 0; + }else{ + if (recv (f, rpkt->dat, sizeof(rpkt->dat), 0) < 0) { + if (krb_debug) + krb_warning("recvfrom: errno = %d\n", errno); + return 0; + } + } + return 1; +} + +static int +send_recv(KTEXT pkt, KTEXT rpkt, int proto, struct sockaddr_in *_to, + struct host *addrs, int n_hosts) +{ + int f; + int ret = 0; + if(proto == IPPROTO_UDP) + f = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + else if(proto == IPPROTO_TCP) + f = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + else{ + krb_warning("Unknown protocol `%d'.\n", proto); + return 0; + } + + if(connect(f, (struct sockaddr*)_to, sizeof(*_to)) < 0) + krb_warning("Connecting socket: errno = %d\n", errno); + else + ret = send_recv_it(pkt, rpkt, proto == IPPROTO_TCP, f, + _to, addrs, n_hosts); + + close(f); + return ret; +} + |