discuss@menelaus.mit.edu: [1708] in Kerberos-V5-bugs [1708] in Kerberos-V5-bugs home help back first fref pref prev next nref lref last post kdc_time stuff daemon@ATHENA.MIT.EDU (Howard Chu) Sat Nov 4 20:47:18 1995 From: hyc@locus.com (Howard Chu)
To: krb5-bugs@MIT.EDU
Date: Sat, 04 Nov 95 17:49:22 -0800
Here's a quick ripoff of send_to_kdc for Kerberos 4 and 5 that obtains the
KDC's notion of the current time. I would have preferred to have only written
this code once, and have both the K4 and K5 libraries call it, but it was too
much effort to figure out what could be stripped and streamlined. So it goes.
I suppose this won't find much use in paranoid environments, since there's no
authentication of the time service, but it's nice for PCs...
Howard Chu Principal Member of Technical Staff
hyc@locus.com Locus Computing Corporation
#--------------------------------CUT HERE-------------------------------------
#! /bin/sh
#
# This is a shell archive. Save this into a file, edit it
# and delete all lines above this comment. Then give this
# file to sh by executing the command "sh file". The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# -rw-r--r-- 1 hyc pdev 8671 Oct 20 20:40 krb4/kdc_time.c
# -rw-r--r-- 1 hyc pdev 7331 Oct 20 20:25 krb5/os/kdctime.c
#
echo 'x - kdc_time.c'
if test -f kdc_time.c; then echo 'shar: not overwriting kdc_time.c'; else
sed 's/^X//' << '________This_Is_The_END________' > kdc_time.c
X/*
X * kdc_time.c
X *
X */
X
X#define DEFINE_SOCKADDR /* Ask for sockets declarations from krb.h. */
X#include "krb.h"
X#include "krbports.h"
X#include "prot.h"
X#include
X#include
X#include
X#ifndef MAXHOSTNAMELEN
X#define MAXHOSTNAMELEN 64
X#endif
X
X#define S_AD_SZ sizeof(struct sockaddr_in)
X
X#ifdef POSIX
X#include
X#else
Xextern char *malloc(), *calloc(), *realloc();
X#endif
X
X/* ANL - On AIX this does not compile, so add the select.h */
X#ifndef FD_SET
X#include
X#endif
X
Xextern int
Xsend_recv PROTOTYPE ((KTEXT pkt, KTEXT rpkt, SOCKET f,
X struct sockaddr_in *_to, struct hostent *addrs));
X
X/*
X * krb_kdc_time() sends a time request to the Kerberos authentication
X * server(s) in the given realm and returns the reply.
X * The "realm" argument indicates the realm of the Kerberos server(s)
X * to transact with. If the realm is null, the local realm is used.
X *
X * If more than one Kerberos server is known for a given realm,
X * different servers will be queried until one of them replies.
X * Several attempts (retries) are made for each server before
X * giving up entirely.
X *
X * The following results can be returned:
X *
X * KSUCCESS - an answer was received from a Kerberos host
X *
X * SKDC_CANT - can't get local realm
X * - can't find "kerberos" in /etc/services database
X * - can't open socket
X * - can't bind socket
X * - all ports in use
X * - couldn't find any Kerberos host
X *
X * SKDC_RETRY - couldn't get an answer from any Kerberos server,
X * after several retries
X */
X
X#define TIMEPORT 37 /* RFC 868 */
X#define NTP_PORT 123 /* RFC 1305 */
X#define NET_EPOCH 2208988800 /* RFC 868 */
X
Xtypedef unsigned KRB4_32 U32;
X
Xtypedef struct ntp_msg {
X unsigned char mode, stratum, poll, precision;
X U32 root_delay;
X U32 root_dispersion;
X U32 ref_id;
X U32 ref_time[2];
X U32 org_time[2];
X U32 rcv_time[2];
X U32 xmt_time[2];
X /* char auth[12]; */
X} ntp_msg;
X
Xint INTERFACE
Xkrb_kdc_time(realm, server_time)
X char *realm;
X KRB4_32 *server_time;
X{
X int i;
X SOCKET f;
X int no_host; /* was a kerberos host found? */
X int retry;
X int n_hosts;
X int retval;
X struct sockaddr_in to;
X struct hostent FAR *farkedhost;
X struct hostent *host, *hostlist;
X char *cp;
X char krbhst[MAXHOSTNAMELEN];
X char lrealm[REALM_SZ];
X char *scol;
X int timeport = htons(TIMEPORT);
X int ntp_port = htons(NTP_PORT);
X KTEXT_ST req, rep;
X ntp_msg *reqp = (ntp_msg *)req.dat, *repp = (ntp_msg *)rep.dat;
X U32 nettime;
X KRB4_32 *tmptime;
X
X /*
X * If "realm" is non-null, use that, otherwise get the
X * local realm.
X */
X if (realm)
X (void) strcpy(lrealm, realm);
X else
X if (krb_get_lrealm(lrealm,1)) {
X DEB (("%s: can't get local realm\n", prog));
X return(SKDC_CANT);
X }
X DEB (("lrealm is %s\n", lrealm));
X
X if (SOCKET_INITIALIZE()) {
X DEB (("%s: can't initialize sockets library\n",prog));
X return (SKDC_CANT);
X }
X /* from now on, exit through rtn label for cleanup */
X
X memset((char *)&to, 0, S_AD_SZ);
X hostlist = (struct hostent *) malloc(sizeof(struct hostent));
X if (!hostlist) {
X retval = /*errno */SKDC_CANT;
X goto rtn_clean; /* Run SOCKET_CLEANUP then return. */
X }
X hostlist->h_name = 0; /* so it gets properly freed at "rtn" */
X
X f = socket(AF_INET, SOCK_DGRAM, 0);
X if (f == INVALID_SOCKET) {
X DEB (("%s: Can't open socket\n", prog));
X retval = /*errno */SKDC_CANT;
X goto rtn_clean; /* Run SOCKET_CLEANUP then return. */
X }
X
X/*
X** FIXME! FTP Software's WINSOCK implmentation insists that
X** a socket be bound before it can receive datagrams.
X** This is outside specs. Since it shouldn't hurt any
X** other implementations we'll go ahead and do it for
X** now.
X*/
X {
X struct sockaddr_in from;
X memset ((char *)&from, 0, S_AD_SZ);
X from.sin_family = AF_INET;
X from.sin_addr.s_addr = INADDR_ANY;
X if ( bind(f, (struct sockaddr *)&from, S_AD_SZ) == SOCKET_ERROR ) {
X DEB (("%s : Can't bind\n", prog));
X retval = SKDC_CANT;
X goto rtn;
X }
X }
X/* End of kludge (FIXME) for FTP Software WinSock stack. */
X
X memset(req.dat, 0, sizeof(ntp_msg));
X reqp->mode = 0x0b; /* Version 1, Client mode */
X no_host = 1;
X /* get an initial allocation */
X n_hosts = 0;
X for (i = 1; krb_get_krbhst(krbhst, lrealm, i) == KSUCCESS; ++i) {
X#ifdef DEBUG
X if (krb_debug) {
X DEB (("Getting host entry for %s...",krbhst));
X (void) fflush(stdout);
X }
X#endif
X if (0 != (scol = strchr(krbhst,':')))
X *scol = '\0';
X farkedhost = gethostbyname(krbhst);
X#ifdef DEBUG
X if (krb_debug) {
X DEB (("%s.\n", farkedhost ? "Got it" : "Didn't get it"));
X (void) fflush(stdout);
X }
X#endif
X if (!farkedhost)
X continue;
X no_host = 0; /* found at least one */
X n_hosts++;
X /* preserve host network address to check later
X * (would be better to preserve *all* addresses,
X * take care of that later)
X */
X hostlist = (struct hostent *)
X realloc((char *)hostlist,
X (unsigned)
X sizeof(struct hostent)*(n_hosts+1));
X if (!hostlist) {
X retval = /*errno */SKDC_CANT;
X goto rtn;
X }
X hostlist[n_hosts-1] = *farkedhost; /* Copy into array */
X memset((char *)&hostlist[n_hosts], 0, sizeof(struct hostent));
X host = &hostlist[n_hosts-1];
X cp = malloc((unsigned)host->h_length);
X if (!cp) {
X retval = /*errno */SKDC_CANT;
X goto rtn;
X }
X _fmemcpy(cp, host->h_addr, host->h_length);
X
X/* At least Sun OS version 3.2 (or worse) and Ultrix version 2.2
X (or worse) only return one name ... */
X#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40))
X host->h_addr_list = (char **)malloc(sizeof(char *));
X if (!host->h_addr_list) {
X retval = /*errno */SKDC_CANT;
X goto rtn;
X }
X#endif /* ULTRIX022 || SunOS */
X host->h_addr = cp;
X to.sin_family = host->h_addrtype;
X memcpy((char *)&to.sin_addr, host->h_addr,
X host->h_length);
X to.sin_port = timeport;
X req.length = 0;
X rep.length = sizeof(rep.dat);
X if (send_recv(&req, &rep, f, &to, hostlist)) {
X retval = KSUCCESS;
X tmptime = (KRB4_32 *)&rep.dat;
X goto rtn;
X }
X to.sin_port = ntp_port;
X req.length = sizeof(ntp_msg);
X rep.length = sizeof(rep.dat);
X if (send_recv(&req, &rep, f, &to, hostlist)) {
X /* Check validity of reply */
X if ((repp->mode & 0xc0) == 0xc0 || repp->stratum < 1 ||
X repp->stratum > 15 || (repp->xmt_time[0] == 0 &&
X repp->xmt_time[1] == 0))
X continue;
X retval = KSUCCESS;
X tmptime = (KRB4_32 *)&repp->xmt_time[0];
X goto rtn;
X }
X DEB (("Timeout, error, or wrong descriptor\n"));
X }
X if (no_host) {
X DEB (("%s: can't find any Kerberos host.\n", prog));
X retval = SKDC_CANT;
X goto rtn;
X }
X
X /* retry each host in sequence */
X for (retry = 0; retry < CLIENT_KRB_RETRY; ++retry) {
X for (host = hostlist; host->h_name != (char *)NULL; host++) {
X to.sin_family = host->h_addrtype;
X memcpy((char *)&to.sin_addr, host->h_addr,
X host->h_length);
X to.sin_port = timeport;
X req.length = 0;
X rep.length = sizeof(rep.dat);
X if (send_recv(&req, &rep, f, &to, hostlist)) {
X retval = KSUCCESS;
X tmptime = (KRB4_32 *)&rep.dat;
X goto rtn;
X }
X to.sin_port = ntp_port;
X req.length = sizeof(ntp_msg);
X rep.length = sizeof(rep.dat);
X if (send_recv(&req, &rep, f, &to, hostlist)) {
X /* Check validity of reply */
X if ((repp->mode & 0xc0) == 0xc0 || repp->stratum < 1 ||
X repp->stratum > 15 || (repp->xmt_time[0] == 0 &&
X repp->xmt_time[1] == 0))
X continue;
X retval = KSUCCESS;
X tmptime = (KRB4_32 *)&repp->xmt_time[0];
X goto rtn;
X }
X }
X }
X retval = SKDC_RETRY;
Xrtn:
X (void) closesocket (f);
X if (retval == KSUCCESS)
X {
X nettime = ntohl(*tmptime);
X nettime -= NET_EPOCH;
X *server_time = nettime;
X }
Xrtn_clean:
X SOCKET_CLEANUP(); /* Done with using sockets for awhile */
X if (hostlist) {
X register struct hostent *hp;
X for (hp = hostlist; hp->h_name; hp++)
X#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40))
X if (hp->h_addr_list) {
X#endif /* ULTRIX022 || SunOS */
X if (hp->h_addr)
X free(hp->h_addr);
X#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40))
X free((char *)hp->h_addr_list);
X }
X#endif /* ULTRIX022 || SunOS */
X free((char *)hostlist);
X }
X return(retval);
X}
________This_Is_The_END________
if test `wc -c < kdc_time.c` -ne 8671; then
echo 'shar: kdc_time.c was damaged during transit (should have been 8671 bytes)'
fi
fi ; : end of overwriting check
echo 'x - kdctime.c'
if test -f kdctime.c; then echo 'shar: not overwriting kdctime.c'; else
sed 's/^X//' << '________This_Is_The_END________' > kdctime.c
X/*
X * lib/krb5/os/kdctime.c
X *
X * Try to obtain KDC server's idea of current time, using Time
X * protocol (RFC 868) or Simple Network Time Protocol (RFC 1769).
X */
X
X#define NEED_SOCKETS
X#define NEED_LOWLEVEL_IO
X#include "k5-int.h"
X
X#ifdef HAVE_SYS_TIME_H
X#include
X#else
X#include
X#endif
X#include "os-proto.h"
X
X#ifdef _AIX
X#include
X#endif
X
X/*
X * send a time protocol request to a KDC for realm 'realm' and
X * return the resulting unix time in 'srvtime'.
X *
X * If the message is sent and a response is received, 0 is returned,
X * otherwise an error code is returned.
X */
X
Xextern int krb5_max_skdc_timeout;
Xextern int krb5_skdc_timeout_shift;
Xextern int krb5_skdc_timeout_1;
X
X#define TIME_PORT 37
X#define NTP_PORT 123
X
X/* Unix time is from Jan 1 1970, Network time is from Jan 1 1900 */
X#define NET_EPOCH 2208988800L
X
Xtypedef struct ntp_msg {
X unsigned char mode, stratum, poll, precision;
X krb5_ui_4 root_delay;
X krb5_ui_4 root_disp;
X krb5_ui_4 ref_id;
X krb5_ui_4 ref_time[2];
X krb5_ui_4 org_time[2];
X krb5_ui_4 rcv_time[2];
X krb5_ui_4 xmt_time[2];
X /* char auth[12]; */
X} ntp_msg;
X
Xint
Xsend_time_request (krb5_data *req, krb5_data *rep, struct sockaddr *addr,
X SOCKET *socklist, int naddr, u_short port);
X
Xkrb5_error_code INTERFACE
Xkrb5_kdc_time (context, realm, srvtime)
X krb5_context context;
X const krb5_data * realm;
X krb5_int32 * srvtime;
X{
X struct sockaddr *addr;
X int naddr, i;
X krb5_error_code retval;
X SOCKET *socklist;
X krb5_ui_4 nettime;
X krb5_int32 *tmptime;
X u_short timeport = htons(TIME_PORT);
X u_short ntp_port = htons(NTP_PORT);
X char repbuf[sizeof(ntp_msg)+12];
X ntp_msg message, *reply = (ntp_msg *)repbuf;
X krb5_data req, rep;
X
X /*
X * find KDC location(s) for realm
X */
X
X if (retval = krb5_locate_kdc (context, realm, &addr, &naddr))
X return retval;
X if (naddr == 0)
X return KRB5_REALM_UNKNOWN;
X
X socklist = (SOCKET *)malloc(naddr * sizeof(SOCKET));
X if (socklist == NULL) {
X krb5_xfree(addr);
X return ENOMEM;
X }
X if (SOCKET_INITIALIZE()) { /* PC needs this for some tcp/ip stacks */
X krb5_xfree(addr);
X krb5_xfree(socklist);
X return SOCKET_ERRNO;
X }
X
X req.data = (char FAR *)&message;
X rep.data = repbuf;
X
X /* Try Time protocol first */
X req.length = 0;
X rep.length = sizeof(repbuf);
X i = send_time_request(&req, &rep, addr, socklist, naddr, timeport);
X if (i == 0) {
X tmptime = (krb5_int32 *)repbuf;
X } else {
X /* Ok, try SNTP now */
X req.length = sizeof(ntp_msg);
X memset(&message, 0, sizeof(message));
X message.mode = 0x0b; /* Version 1, client mode */
X rep.length = sizeof(repbuf);
X i = send_time_request(&req, &rep, addr, socklist, naddr, ntp_port);
X if (i == 0 && (reply->mode & 0xc0) != 0xc0 && reply->stratum >= 1 &&
X reply->stratum <= 15 &&
X (reply->xmt_time[0] | reply->xmt_time[1]))
X tmptime = (krb5_int32 *)&reply->xmt_time;
X else
X i = -1;
X }
X if (i == 0) {
X nettime = ntohl(*tmptime);
X nettime -= NET_EPOCH;
X *srvtime = nettime;
X }
X SOCKET_CLEANUP(); /* Done with sockets for now */
X krb5_xfree(addr);
X krb5_xfree(socklist);
X return i;
X}
X
Xint
Xsend_time_request(krb5_data *req, krb5_data *rep, struct sockaddr *addr,
X SOCKET *socklist, int naddr, u_short port) {
X register int timeout, host, i;
X int sent, nready;
X krb5_error_code retval;
X fd_set readable;
X struct timeval waitlen;
X int cc;
X struct sockaddr_in *sin_p;
X struct in_addr last_addr;
X
X for (i = 0; i < naddr; i++)
X socklist[i] = INVALID_SOCKET;
X
X memset(&last_addr, 0, sizeof(last_addr));
X
X /*
X * do exponential backoff.
X */
X
X for (timeout = krb5_skdc_timeout_1; timeout < krb5_max_skdc_timeout;
X timeout <<= krb5_skdc_timeout_shift) {
X sent = 0;
X /* send to the host, wait timeout seconds for a response,
X then move on. */
X for (host = 0; host < naddr; host++) {
X /* locate_kdc may give the same host address twice, once on
X * primary port and once on secondary. Ignore second entry.
X */
X sin_p = (struct sockaddr_in *)&addr[host];
X if (!memcmp(&last_addr, &sin_p->sin_addr, sizeof(last_addr)))
X continue;
X memcpy(&last_addr, &sin_p->sin_addr, sizeof(last_addr));
X sin_p->sin_port = port;
X /* cache some sockets for each host */
X if (socklist[host] == INVALID_SOCKET) {
X /* XXX 4.2/4.3BSD has PF_xxx = AF_xxx, so the socket
X creation here will work properly... */
X /*
X * From socket(2):
X *
X * The protocol specifies a particular protocol to be
X * used with the socket. Normally only a single
X * protocol exists to support a particular socket type
X * within a given protocol family.
X */
X socklist[host] = socket(addr[host].sa_family, SOCK_DGRAM, 0);
X if (socklist[host] == INVALID_SOCKET)
X continue; /* try other hosts */
X /* have a socket to send/recv from */
X /* On BSD systems, a connected UDP socket will get connection
X refused and net unreachable errors while an unconnected
X socket will time out, so use connect, send, recv instead of
X sendto, recvfrom. The connect here may return an error if
X the destination host is known to be unreachable. */
X if (connect(socklist[host],
X &addr[host], sizeof(addr[host])) == SOCKET_ERROR)
X continue;
X }
X if (send(socklist[host], req->data, req->length, 0) !=
X req->length)
X continue;
X retry:
X waitlen.tv_usec = 0;
X waitlen.tv_sec = timeout;
X FD_ZERO(&readable);
X FD_SET(socklist[host], &readable);
X if (nready = select(SOCKET_NFDS(socklist[host]),
X &readable,
X 0,
X 0,
X &waitlen)) {
X if (nready == SOCKET_ERROR) {
X if (SOCKET_ERRNO == SOCKET_EINTR)
X goto retry;
X retval = SOCKET_ERRNO;
X break;
X }
X if ((cc = recv(socklist[host], rep->data, rep->length, 0))
X == SOCKET_ERROR)
X {
X /* man page says error could be:
X EBADF: won't happen
X ENOTSOCK: it's a socket.
X EWOULDBLOCK: not marked non-blocking, and we selected.
X EINTR: could happen
X EFAULT: we allocated the reply packet.
X
X In addition, net related errors like ECONNREFUSED
X are possble (but undocumented). Assume anything
X other than EINTR is a permanent error for the
X server (i.e. don't set sent = 1).
X */
X
X if (SOCKET_ERRNO == SOCKET_EINTR)
X sent = 1;
X continue;
X }
X
X /* We might consider here verifying that the reply
X came from one of the KDC's listed for that address type,
X but that check can be fouled by some implementations of
X some network types which might show a loopback return
X address, for example, if the KDC is on the same host
X as the client. */
X
X retval = 0;
X break;
X } else if (nready == 0) {
X /* timeout */
X sent = 1;
X }
X /* not ready, go on to next server */
X }
X if (!sent) {
X /* never connected to any time servers */
X break;
X }
X }
X for (i = 0; i < naddr; i++)
X if (socklist[i] != INVALID_SOCKET)
X (void)closesocket (socklist[i]);
X return retval;
X}
________This_Is_The_END________
if test `wc -c < kdctime.c` -ne 7331; then
echo 'shar: kdctime.c was damaged during transit (should have been 7331 bytes)'
fi
fi ; : end of overwriting check
exit 0
home help back first fref pref prev next nref lref last post