⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 krb4-security.c

📁 开源备份软件源码 AMANDA, the Advanced Maryland Automatic Network Disk Archiver, is a backup system that a
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Amanda, The Advanced Maryland Automatic Network Disk Archiver * Copyright (c) 1993,1999 University of Maryland * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, 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 U.M. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission.  U.M. makes no representations about the * suitability of this software for any purpose.  It is provided "as is" * without express or implied warranty. * * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: the Amanda Development Team.  Its members are listed in a * file named AUTHORS, in the root directory of this distribution. *//* * $Id: krb4-security.c,v 1.18 2006/07/13 03:22:20 paddy_s Exp $ * * krb4-security.c - helper functions for kerberos v4 security. */#include "config.h"#include <des.h>#include <krb.h>#include "amanda.h"#include "dgram.h"#include "event.h"#include "packet.h"#include "queue.h"#include "security.h"#include "security-util.h"#include "protocol.h"#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_in6 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 */    in_port_t 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 */#ifndef STREAM_BUFSIZE#define	STREAM_BUFSIZE	(MAX_TAPE_BLOCK_BYTES * 2)#endif/* * 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,    NULL,    NULL};/* * 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];    unsigned 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 knet_write(int, const void *, size_t);static int knet_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 *kpkthdr2str(const struct krb4_handle *, const pkt_t *);static int str2kpkthdr(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 *);#define HOSTNAME_INSTANCE inststatic char *ticketfilename = NULL;static voidkilltickets(void){    if (ticketfilename != NULL)	unlink(ticketfilename);    amfree(ticketfilename);}/* * Setup some things about krb4.  This should only be called once. */static voidinit(void){    char tktfile[256];    in_port_t port;    static int beenhere = 0;    if (beenhere)	return;    beenhere = 1;    gethostname(hostname, SIZEOF(hostname) - 1);    hostname[SIZEOF(hostname) - 1] = '\0';    if (atexit(killtickets) < 0)	error(_("could not setup krb4 exit handler: %s"), strerror(errno));    /*     * [XXX] It could be argued that if KRBTKFILE is set outside of amanda,     * that it's value should be used instead of us setting one up.     * This file also needs to be removed so that no extra tickets are     * hanging around.     */    g_snprintf(tktfile, SIZEOF(tktfile), "/tmp/tkt%ld-%ld.amanda",	(long)getuid(), (long)getpid());    ticketfilename = stralloc(tktfile);    unlink(ticketfilename);    krb_set_tkt_string(ticketfilename);#if defined(HAVE_PUTENV)    {	char *tkt_env = stralloc2("KRBTKFILE=", ticketfilename);	putenv(tkt_env);    }#else    setenv("KRBTKFILE", ticketfile, 1);#endif    dgram_zero(&netfd);    dgram_bind(&netfd, &port);}/* * Get a ticket granting ticket and stuff it in the cache */static voidget_tgt(void){    char realm[REALM_SZ];    int rc;    strncpy(realm, krb_realmofhost(hostname), SIZEOF(realm) - 1);    realm[SIZEOF(realm) - 1] = '\0';    rc = krb_get_svc_in_tkt(SERVER_HOST_PRINCIPAL, SERVER_HOST_INSTANCE,	realm, "krbtgt", realm, TICKET_LIFETIME, SERVER_HOST_KEY_FILE);    if (rc != 0) {	error(_("could not get krbtgt for %s.%s@%s from %s: %s"),	    SERVER_HOST_PRINCIPAL, SERVER_HOST_INSTANCE, realm,	    SERVER_HOST_KEY_FILE, krb_err_txt[rc]);    }    krb_set_lifetime(TICKET_LIFETIME);}/* * krb4 version of a security handle allocator.  Logically sets * up a network "connection". */static voidkrb4_connect(    const char *hostname,    char *	(*conf_fn)(char *, void *),    void 	(*fn)(void *, security_handle_t *, security_status_t),    void *	arg,    void *	datap){    struct krb4_handle *kh;    char handle[32];    struct servent *se;    struct hostent *he;    in_port_t port;    (void)conf_fn;	/* Quiet unused parameter warning */    (void)datap;	/* Quiet unused parameter warning */    assert(hostname != NULL);    /*     * Make sure we're initted     */    init();    kh = alloc(SIZEOF(*kh));    security_handleinit(&kh->sech, &krb4_security_driver);    if ((he = gethostbyname(hostname)) == NULL) {	security_seterror(&kh->sech,	    _("%s: could not resolve hostname"), hostname);	(*fn)(arg, &kh->sech, S_ERROR);	return;    }    if ((se = getservbyname(KAMANDA_SERVICE_NAME, "udp")) == NULL)	port = (int)KAMANDA_SERVICE_DEFAULT;    else	port = ntohs(se->s_port);    g_snprintf(handle, SIZEOF(handle), "%ld", (long)time(NULL));    inithandle(kh, he, (int)port, handle);    (*fn)(arg, &kh->sech, S_OK);}/* * Setup to handle new incoming connections */static voidkrb4_accept(    const struct security_driver *driver,    int		in,    int		out,    void	(*fn)(security_handle_t *, pkt_t *)){    (void)driver;	/* Quiet unused parameter warning */    (void)out;		/* Quiet unused parameter warning */    /*     * Make sure we're initted     */    init();    /*     * We assume that in and out both point to the same socket     */    dgram_socket(&netfd, in);    /*     * Assign the function and return.  When they call recvpkt later,     * the recvpkt callback will call this function when it discovers     * new incoming connections     */    accept_fn = fn;    if (ev_netfd == NULL)	ev_netfd = event_register((event_id_t)netfd.socket, EV_READFD,			    recvpkt_callback, NULL);}/* * Given a hostname and a port, setup a krb4_handle */static voidinithandle(    struct krb4_handle *kh,    struct hostent *	he,    int			port,    const char *	handle){    /*     * Get the instance and realm for this host     * (krb_realmofhost always returns something)     */    krb4_getinst(he->h_name, kh->inst, SIZEOF(kh->inst));    strncpy(kh->realm, krb_realmofhost(he->h_name), SIZEOF(kh->realm) - 1);    kh->realm[SIZEOF(kh->realm) - 1] = '\0';    /*     * Save a copy of the hostname     */    strncpy(kh->hostname, he->h_name, SIZEOF(kh->hostname) - 1);    kh->hostname[SIZEOF(kh->hostname) - 1] = '\0';    /*     * We have no checksum or session key at this point     */    kh->cksum = 0;    memset(kh->session_key, 0, SIZEOF(kh->session_key));    /*     * Setup our peer info.  We don't do anything with the sequence yet,     * so just leave it at 0.     */    kh->peer.sin6_family = (sa_family_t)AF_INET6;    kh->peer.sin6_port = (in_port_t)port;    kh->peer.sin6_addr = *(struct in6_addr *)he->h_addr;    strncpy(kh->proto_handle, handle, SIZEOF(kh->proto_handle) - 1);    kh->proto_handle[SIZEOF(kh->proto_handle) - 1] = '\0';    kh->sequence = 0;    kh->fn = NULL;    kh->arg = NULL;    kh->ev_timeout = NULL;}/* * frees a handle allocated by the above */static voidkrb4_close(    void *	inst){    krb4_recvpkt_cancel(inst);    amfree(inst);}/* * Transmit a packet.  Add security information first. */static ssize_tkrb4_sendpkt(    void *	cookie,    pkt_t *	pkt){    struct krb4_handle *kh = cookie;    assert(kh != NULL);    assert(pkt != NULL);    /*     * Initialize this datagram     */    dgram_zero(&netfd);    /*     * Add the header to the packet     */    dgram_cat(&netfd, kpkthdr2str(kh, pkt));    /*     * Add the security info.  This depends on which kind of packet we're     * sending.     */    switch (pkt->type) {    case P_REQ:	/*	 * Requests get sent with a ticket embedded in the header.  The	 * checksum is generated from the contents of the body.	 */	if (add_ticket(kh, pkt, &netfd) < 0)	    return (-1);	break;    case P_REP:	/*	 * Replies get sent with a mutual authenticator added.  The	 * mutual authenticator is the encrypted checksum from the	 * ticket + 1	 */	add_mutual_auth(kh, &netfd);	break;    case P_ACK:    case P_NAK:    default:	/*	 * The other types have no security stuff added for krb4.	 * Shamefull.	 */	break;    }    /*     * Add the body, and send it     */    dgram_cat(&netfd, pkt->body);    if (dgram_send_addr(&kh->peer, &netfd) != 0) {	security_seterror(&kh->sech,	    _("send %s to %s failed: %s"), pkt_type2str(pkt->type),	    kh->hostname, strerror(errno));	return (-1);    }    return (0);}/* * Set up to receive a packet asyncronously, and call back when * it has been read. */static voidkrb4_recvpkt(    void *	cookie,    void	(*fn)(void *, pkt_t *, security_status_t),    void *	arg,    int		timeout){    struct krb4_handle *kh = cookie;    assert(netfd.socket >= 0);    assert(kh != NULL);    /*     * We register one event handler for our network fd which takes     * care of all of our async requests.  When all async requests     * have either been satisfied or cancelled, we unregister our     * network event handler.     */    if (ev_netfd == NULL) {	assert(handleq.qlength == 0);	ev_netfd = event_register((event_id_t)netfd.socket, EV_READFD,			    recvpkt_callback, NULL);    }    /*     * Multiple recvpkt calls override previous ones     * If kh->fn is NULL then it is not in the queue.     */    if (kh->fn == NULL)	handleq_add(kh);    if (kh->ev_timeout != NULL)	event_release(kh->ev_timeout);    if (timeout < 0)	kh->ev_timeout = NULL;    else	kh->ev_timeout = event_register((event_id_t)timeout, EV_TIME,				recvpkt_timeout, kh);    kh->fn = fn;    kh->arg = arg;}/* * Remove a async receive request from the queue * If it is the last one to be removed, then remove the event handler

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -