+#include "stream.h"
+#include "version.h"
+
+
+/*
+ * If you don't have atexit() or on_exit(), you could just consider
+ * making atexit() empty and clean up your ticket files some other
+ * way
+ */
+#ifndef HAVE_ATEXIT
+#ifdef HAVE_ON_EXIT
+#define atexit(func) on_exit(func, 0)
+#else
+#define atexit(func) (you must to resolve lack of atexit)
+#endif /* HAVE_ON_EXIT */
+#endif /* ! HAVE_ATEXIT */
+
+int krb_set_lifetime(int);
+int kuserok(AUTH_DAT *, char *);
+
+/*
+ * This is the private handle data
+ */
+struct krb4_handle {
+ security_handle_t sech; /* MUST be first */
+ struct sockaddr_in peer; /* host on other side */
+ char hostname[MAX_HOSTNAME_LENGTH+1]; /* human form of above */
+ char proto_handle[32]; /* protocol handle for this req */
+ int sequence; /* last sequence number we received */
+ char inst[INST_SZ]; /* krb4 instance form of above */
+ char realm[REALM_SZ]; /* krb4 realm of this host */
+ unsigned long cksum; /* cksum of the req packet we sent */
+ des_cblock session_key; /* session key */
+
+ /*
+ * The rest is used for the async recvpkt/recvpkt_cancel
+ * interface.
+ */
+ void (*fn)(void *, pkt_t *, security_status_t);
+ /* func to call when packet recvd */
+ void *arg; /* argument to pass function */
+ event_handle_t *ev_timeout; /* timeout handle for recv */
+ TAILQ_ENTRY(krb4_handle) tq; /* queue handle */
+};
+
+/*
+ * This is the internal security_stream data for krb4.
+ */
+struct krb4_stream {
+ security_stream_t secstr; /* MUST be first */
+ struct krb4_handle *krb4_handle; /* pointer into above */
+ int fd; /* io file descriptor */
+ int port; /* local port this is bound to */
+ int socket; /* fd for server-side accepts */
+ event_handle_t *ev_read; /* read event handle */
+ char databuf[MAX_TAPE_BLOCK_BYTES]; /* read buffer */
+ int len; /* */
+ void (*fn)(void *, void *, ssize_t);/* read event fn */
+ void *arg; /* arg for previous */
+};
+
+/*
+ * This is the tcp stream buffer size
+ */
+#define STREAM_BUFSIZE (MAX_TAPE_BLOCK_BYTES * 2)
+
+/*
+ * Interface functions
+ */
+static void krb4_connect(const char *, char *(*)(char *, void *),
+ void (*)(void *, security_handle_t *, security_status_t),
+ void *, void *);
+static void krb4_accept(const struct security_driver *, int, int, void (*)(security_handle_t *, pkt_t *));
+static void krb4_close(void *);
+static int krb4_sendpkt(void *, pkt_t *);
+static void krb4_recvpkt(void *, void (*)(void *, pkt_t *, security_status_t),
+ void *, int);
+static void krb4_recvpkt_cancel(void *);
+static void * krb4_stream_server(void *);
+static int krb4_stream_accept(void *);
+static void * krb4_stream_client(void *, int);
+static void krb4_stream_close(void *);
+static int krb4_stream_auth(void *);
+static int krb4_stream_id(void *);
+static int krb4_stream_write(void *, const void *, size_t);
+static void krb4_stream_read(void *, void (*)(void *, void *, int), void *);
+static int krb4_stream_read_sync(void *, void **);
+static void krb4_stream_read_cancel(void *);
+
+
+/*
+ * This is our interface to the outside world.
+ */
+const security_driver_t krb4_security_driver = {
+ "KRB4",
+ krb4_connect,
+ krb4_accept,
+ krb4_close,
+ krb4_sendpkt,
+ krb4_recvpkt,
+ krb4_recvpkt_cancel,
+ krb4_stream_server,
+ krb4_stream_accept,
+ krb4_stream_client,
+ krb4_stream_close,
+ krb4_stream_auth,
+ krb4_stream_id,
+ krb4_stream_write,
+ krb4_stream_read,
+ krb4_stream_read_sync,
+ krb4_stream_read_cancel,
+ sec_close_connection_none,
+};
+
+/*
+ * Cache the local hostname
+ */
+static char hostname[MAX_HOSTNAME_LENGTH+1];
+
+/*
+ * This is the dgram_t that we use to send and recv protocol packets
+ * over the net. There is only one per process, so it lives globally
+ * here.
+ */
+static dgram_t netfd;
+
+/*
+ * This is a queue of outstanding async requests
+ */
+static struct {
+ TAILQ_HEAD(, krb4_handle) tailq;
+ int qlength;
+} handleq = {
+ TAILQ_HEAD_INITIALIZER(handleq.tailq), 0
+};
+
+/*
+ * Macros to add or remove krb4_handles from the above queue
+ */
+#define handleq_add(kh) do { \
+ assert(handleq.qlength == 0 ? TAILQ_FIRST(&handleq.tailq) == NULL : 1); \
+ TAILQ_INSERT_TAIL(&handleq.tailq, kh, tq); \
+ handleq.qlength++; \
+} while (0)
+
+#define handleq_remove(kh) do { \
+ assert(handleq.qlength > 0); \
+ assert(TAILQ_FIRST(&handleq.tailq) != NULL); \
+ TAILQ_REMOVE(&handleq.tailq, kh, tq); \
+ handleq.qlength--; \
+ assert((handleq.qlength == 0) ^ (TAILQ_FIRST(&handleq.tailq) != NULL)); \
+} while (0)
+
+#define handleq_first() TAILQ_FIRST(&handleq.tailq)
+#define handleq_next(kh) TAILQ_NEXT(kh, tq)
+
+
+/*
+ * This is the event manager's handle for our netfd
+ */
+static event_handle_t *ev_netfd;
+
+/*
+ * This is a function that should be called if a new security_handle_t is
+ * created. If NULL, no new handles are created.
+ * It is passed the new handle and the received pkt
+ */
+static void (*accept_fn)(security_handle_t *, pkt_t *);
+
+
+/*
+ * This is a structure used in encoding the cksum in a mutual-auth
+ * transaction. The checksum is placed in here first before encryption
+ * because encryption requires at least 8 bytes of data, and an unsigned
+ * long on most machines (32 bit ones) is 4 bytes.
+ */
+union mutual {
+ char pad[8];
+ long cksum;
+};
+
+/*
+ * Private functions
+ */
+static unsigned long krb4_cksum(const char *);
+static void krb4_getinst(const char *, char *, size_t);
+static void host2key(const char *, const char *, des_cblock *);
+static void init(void);
+static void inithandle(struct krb4_handle *, struct hostent *, int,
+ const char *);
+static void get_tgt(void);
+static void killtickets(void);
+static void recvpkt_callback(void *);
+static void recvpkt_timeout(void *);
+static int recv_security_ok(struct krb4_handle *, pkt_t *);
+static void stream_read_callback(void *);
+static void stream_read_sync_callback(void *);
+static int net_write(int, const void *, size_t);
+static int net_read(int, void *, size_t, int);
+
+static int add_ticket(struct krb4_handle *, const pkt_t *, dgram_t *);
+static void add_mutual_auth(struct krb4_handle *, dgram_t *);
+static int check_ticket(struct krb4_handle *, const pkt_t *,
+ const char *, unsigned long);
+static int check_mutual_auth(struct krb4_handle *, const char *);
+
+static const char *pkthdr2str(const struct krb4_handle *, const pkt_t *);
+static int str2pkthdr(const char *, pkt_t *, char *, size_t, int *);
+
+static const char *bin2astr(const unsigned char *, int);
+static void astr2bin(const unsigned char *, unsigned char *, int *);
+
+static void encrypt_data(void *, size_t, des_cblock *);
+static void decrypt_data(void *, size_t, des_cblock *);