File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / freevrrpd / vrrp_ah.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 14 12:01:54 2017 UTC (6 years, 11 months ago) by misho
Branches: freevrrpd, MAIN
CVS tags: v1_1, HEAD
freevrrpd 1.1

/*
 * Copyright (c) 2003 BurnesOnLine <bol@b0l.org>
 *
 * Redistribution and use in source forms, with and without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. Obviously, it
 *    would be nice if you gave credit where credit is due but requiring it
 *    would be too onerous.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Sebastien Petit.
 * 4. Neither the name of its contributors may be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: vrrp_ah.c,v 1.1.1.1 2017/06/14 12:01:54 misho Exp $  
 *
 * MISC COMMENTS :
 * FreeVRRPd project AH implementation using KAME based ipsec stack.
 * tested on :
 *    FreeBSD
 *    NetBSD
 * should work on :
 *    Linux 2.6 or KAME based linux ipsec stack
 *
 * this code use KAME not fully implemented :
 * RFC 2367 - PF_KEY Key Management API, Version 2
 *
 * AUTHORS:
 * this is a working and almost clean "HACK"  :)
 * problem is, we cant set socket wide SPD and SAD
 * need to work out with KAME project to know exactly 
 * the procedure to make process wide changes and NOT
 * host wide changes.
 *
 * b0l.
 * 
 */
#include "vrrp_proto.h"
#include "vrrp_ah.h"
#include "md5.h"

#ifdef ENABLE_VRRP_AH
#ifdef KAME_BASED
/* special struct */
typedef enum {
    HMAC_MD5 = 2,	      /* 128 bits */
    HMAC_SHA1 = 3,	      /* 160 bits */
    HMAC_NULL,
    HMAC_SHA2_256,	      /* 256 bits */
    HMAC_SHA2_384,	      /* 384 bits */
    HMAC_SHA2_512,	      /* 512 bits */
    HMAC_RIPEMD160,	      /* 160 bits */
    AES_XCBC_MAC	      /* 128 bits */
} alg_t;

typedef struct algorithm {
    alg_t type;
    size_t keysize;
} algorithm_t;

algorithm_t algos[] = {
    { HMAC_MD5, 128 },
    { HMAC_SHA1, 160 },
    { HMAC_NULL, 0 },
    { HMAC_SHA2_256, 256 },
    { HMAC_SHA2_384, 384 },
    { HMAC_SHA2_512, 512 },
    { HMAC_RIPEMD160, 160 },
    { AES_XCBC_MAC, 128 }
};

/* what we need 
   ipsec (4)
   setsockopt(2)  * per socket behavior *
   sysctl(3) * host wide *
   ipsec_set_policy(3) * IPsec Policy Control Library (libipsec, -lipsec) *
   */

/* STOLEN FROM FreeBSD setkey.c :) */
struct addrinfo * parse_addr(char *host, char *port) {
	struct addrinfo hints, *res = NULL;
	int error;

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_DGRAM;		/*dummy*/
	hints.ai_protocol = IPPROTO_UDP;	/*dummy*/
	hints.ai_flags = 0;
	error = getaddrinfo(host, port, &hints, &res);
	if (error != 0) {
		perror(gai_strerror(error));
		return NULL;
	}
	return res;
}

int setkeymsg(struct sadb_msg *msg, unsigned int type, unsigned int satype, size_t l) {

	msg->sadb_msg_version = PF_KEY_V2;
	msg->sadb_msg_type = type;
	msg->sadb_msg_errno = 0;
	msg->sadb_msg_satype = satype;
	msg->sadb_msg_reserved = 0;
	msg->sadb_msg_seq = 0;
	msg->sadb_msg_pid = getpid();
	msg->sadb_msg_len = PFKEY_UNIT64(l);
	return 0;
}

int setvarbuf(char *buf, int *off, struct sadb_ext *ebuf, int elen, caddr_t vbuf, int vlen) {
	memset(buf + *off, 0, PFKEY_UNUNIT64(ebuf->sadb_ext_len));
	memcpy(buf + *off, (caddr_t)ebuf, elen);
	memcpy(buf + *off + elen, vbuf, vlen);
	(*off) += PFKEY_ALIGN8(elen + vlen);
	return 0;
}

/* WE NEED :
setkeymsg_spdaddr(type, upper, policy, srcs, splen, dsts, dplen)
*/

/* open the PF_KEY socket */
int vrrp_pfkey_open(void) {
    int key_fd;

    /* opening PF_KEY API */
    key_fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
    if (key_fd < 0) {
	perror("socket(PF_KEY)");
	return key_fd;
    }

    return key_fd;
}

/* close PF_KEY socket */
int vrrp_pfkey_close(int fd) {
    int rc = 0;
    rc = close(fd);
    return rc;
}

/* return -1 on failure, 0 on success */
int vrrp_ah_set_outpolicy(int fd, char *src) {
    int rc = 0;

    rc = vrrp_ah_spd(fd, src, VRRP_OUT_POLICY, SADB_X_SPDADD);
    if (rc < 0) {
	perror("VRRP_OUT_POLICY setup failed!\n");
	rc = -1;
    }
    return rc;
}

/* return -1 on failure, 0 on success */
int vrrp_ah_rm_outpolicy(int fd, char *src) {
    int rc = 0;

    rc = vrrp_ah_spd(fd, src, VRRP_OUT_POLICY, SADB_X_SPDDELETE);
    if (rc < 0) {
	perror("VRRP_OUT_POLICY removal failed\n");
	rc = -1;
    }
    return rc;
}

/* return -1 on failure, 0 on success */
int vrrp_ah_set_inpolicy(int fd, char *src) {
    int rc = 0;

    rc = vrrp_ah_spd(fd, src, VRRP_IN_POLICY, SADB_X_SPDADD);
    if (rc < 0) {
	perror("VRRP_IN_POLICY setup failed\n");
	rc = -1;
    }
    return rc;
}

/* return -1 on failure, 0 on success */
int vrrp_ah_rm_inpolicy(int fd, char *src) {
    int rc = 0;

    rc = vrrp_ah_spd(fd, src, VRRP_IN_POLICY, SADB_X_SPDDELETE);
    if (rc < 0) {
	perror("VRRP_IN_POLICY setup failed\n");
	rc = -1;
    }
    return rc;
}

/* return number of bytes sent to PF_KEY socket/in-kernel */
int vrrp_ah_spd(int fd, char *src_addr, char *ah_policy, unsigned int cmd) {
    /* lets see if it works */
    char *policy;
    int policy_len;
    const int bufsiz = 128 * 1024;
    char * buf = (char *) malloc (bufsiz * sizeof(char));
    struct sadb_msg *msg;
    struct sadb_address m_addr;
    struct sockaddr *sa; 
    struct addrinfo *src, *dst;
    int m_size;
    int salen;
    int rc;

    /* sanity checks */
    if (!buf) {
	fprintf(stderr,"could not allocate memory\n");
	return -1;
    }

    /* prepare the policy */
    policy = ipsec_set_policy(VRRP_OUT_POLICY, strlen(VRRP_OUT_POLICY));
    if (!policy) {
	perror("ipsec_set_policy()");
	return -1;
    }

    policy_len = ipsec_get_policylen(policy);
    if (policy_len < 0) {
	perror("ipsec_get_policylen()");
	return -1;
    }

    /* clearing everything, don't want this bugs to happen again */
    memset(buf,0,bufsiz);
    memset(&m_addr, 0, sizeof(m_addr));

    /* building PF_KEY msg */
    msg = (struct sadb_msg *) buf;
    setkeymsg(msg,(unsigned int)cmd,SADB_SATYPE_UNSPEC, 0);
    m_size = sizeof(struct sadb_msg);

    /* copying the policy */
    memcpy(buf+m_size, policy, policy_len);
    m_size += policy_len;

    /* parsing from / to */
    src = parse_addr(src_addr, 0);
    if (!src) {
	free(policy);
	free(buf);
	return -1;
    }

    dst = parse_addr(VRRP_ADDRESS, 0);
    if (!dst) {
	free(policy);
	freeaddrinfo(src);
	free(buf);
	return -1;
    }

    /* SOURCE SETUP */
    sa = src->ai_addr;
    salen = src->ai_addr->sa_len;
    m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr)+PFKEY_ALIGN8(salen));
    m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
    m_addr.sadb_address_proto = (unsigned int)IPSEC_IPPROTO_ANY;
    m_addr.sadb_address_prefixlen = HOST_MASK; 
    m_addr.sadb_address_reserved = 0;

    setvarbuf(buf, &m_size, (struct sadb_ext *)&m_addr,sizeof(m_addr),(caddr_t)sa, salen);

    /* DESTINATION SETUP */
    sa = dst->ai_addr;
    salen = src->ai_addr->sa_len;
    m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr)+PFKEY_ALIGN8(salen));
    m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
    m_addr.sadb_address_proto = (unsigned int)IPSEC_IPPROTO_ANY;
    m_addr.sadb_address_prefixlen = HOST_MASK; /* (splen >= 0 ? -1 : plen ); */
    m_addr.sadb_address_reserved = 0;

    setvarbuf(buf, &m_size, (struct sadb_ext *)&m_addr,sizeof(m_addr),(caddr_t)sa, salen);

    msg->sadb_msg_len = PFKEY_UNIT64(m_size);

    (void)setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bufsiz, sizeof(bufsiz));
    (void)setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsiz, sizeof(bufsiz));

    rc = send(fd, buf, m_size, 0);
    if (rc < 0)
	perror("could not add entry to SPD: send()");

    free(policy);
    free(buf);
    freeaddrinfo(src);
    freeaddrinfo(dst);
    return rc;
}



/* status = setkeymsg_add(SADB_ADD, $5, $3, $4); */
/* DO NOT FORGET TO CLEAN THOSE FUKING LOCAL STRUCTURE 2 DAYS LOST $#@$!@#$#@!
 * */
int vrrp_ah_sad(int fd, char *src_addr, alg_t algo, char *key) {
    const int bufsiz = 128 * 1024;
    char * buf = (char *) malloc (bufsiz * sizeof(char));
    struct sadb_msg *msg;
    struct sadb_address m_addr;
    struct sockaddr *sa; 
    struct addrinfo *src, *dst, *s;
    int m_size;
    int salen;
    int rc;
    /* ADDED */
    int len;
    char * p_alg_auth = "hmac-sha1";
    char * p_key_auth = "12345678901234567890";
    int p_key_auth_len = strlen(p_key_auth);
    struct sadb_key m_key;
    struct sadb_sa m_sa;
    struct sadb_x_sa2 m_sa2;
    struct sadb_lifetime m_lt;

    msg = (struct sadb_msg *) buf;

    /* clearing the allocated data */
    memset(buf,0,bufsiz);

    setkeymsg(msg, SADB_ADD, SADB_SATYPE_AH, 0);
    /* setkeymsg(msg, SADB_DELETE, SADB_SATYPE_AH, 0); */

    m_size = sizeof(struct sadb_msg);
    /* HACK HACK */
    /* hexdump(msg,m_size); */

    m_key.sadb_key_len = PFKEY_UNIT64(sizeof(m_key) + PFKEY_ALIGN8(p_key_auth_len));
    m_key.sadb_key_exttype = SADB_EXT_KEY_AUTH;
    m_key.sadb_key_bits = p_key_auth_len * 8;
    m_key.sadb_key_reserved = 0;
    
    setvarbuf(buf, &m_size, (struct sadb_ext *)&m_key,sizeof(m_key),(caddr_t)p_key_auth, p_key_auth_len);

    /*
    u_int slen = sizeof(struct sadb_lifetime);
    m_lt.sadb_lifetime_len = PFKEY_UNIT64(slen);
    m_lt.sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
    m_lt.sadb_lifetime_allocations = 0;
    m_lt.sadb_lifetime_bytes = 0;
    m_lt.sadb_lifetime_addtime = 0;
    m_lt.sadb_lifetime_usetime = 0;

    memcpy(buf + m_size, &m_lt, slen);
    m_size += slen; 
    */

    /*
    m_key.sadb_key_len = PFKEY_UNIT64(sizeof(m_key) + PFKEY_ALIGN8(p_key_auth_len));
    m_key.sadb_key_exttype = SADB_EXT_KEY_AUTH;
    m_key.sadb_key_bits = p_key_auth_len * 8;
    m_key.sadb_key_reserved = 0;

    setvarbuf(buf, &m_size, (struct sadb_ext *)&m_key, sizeof(m_key), (caddr_t)p_key_auth, p_key_auth_len);
    */

    len = sizeof(struct sadb_sa);
    m_sa.sadb_sa_len = PFKEY_UNIT64(len);
    m_sa.sadb_sa_exttype = SADB_EXT_SA;
    m_sa.sadb_sa_spi = htonl(0x2710);
    m_sa.sadb_sa_replay = 0;
    m_sa.sadb_sa_state = 0;
    m_sa.sadb_sa_auth = 3; /* hmac-sha1 */
    m_sa.sadb_sa_encrypt = 0; /* no encryption yet */
    m_sa.sadb_sa_flags = 64; /* BUG HERE */

    memcpy(buf + m_size, &m_sa, len);
    m_size += len;

    bzero(&m_sa2, sizeof(struct sadb_x_sa2));

    len = sizeof(struct sadb_x_sa2);
    printf("len: %x  exttype: %x\n",PFKEY_UNIT64(len), SADB_X_EXT_SA2);
    m_sa2.sadb_x_sa2_len = PFKEY_UNIT64(len);
    m_sa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2;
    m_sa2.sadb_x_sa2_mode = IPSEC_MODE_ANY; 
    m_sa2.sadb_x_sa2_reqid = 0;

    memcpy(buf + m_size, &m_sa2, len);
    m_size += len; 

    /* parsing from / to */
    src = parse_addr(src_addr, 0);
    if (!src) {
	free(buf);
	return -1;
    }

    dst = parse_addr(VRRP_ADDRESS, 0);
    if (!dst) {
	freeaddrinfo(src);
	free(buf);
	return -1;
    }

    /* SOURCE SETUP */
    for (s = src; s; s = s->ai_next) {
	sa = s->ai_addr;
	salen = s->ai_addr->sa_len; /* POSSIBLE BUG */
	m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr)+PFKEY_ALIGN8(salen));
	m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
	m_addr.sadb_address_proto = (unsigned int)IPSEC_ULPROTO_ANY;
	m_addr.sadb_address_prefixlen = HOST_MASK; /* (splen >= 0 ? -1 : plen ); */
	m_addr.sadb_address_reserved = 0; 
	setvarbuf(buf, &m_size, (struct sadb_ext *)&m_addr,sizeof(m_addr),(caddr_t)sa, salen);
    }

    /* DESTINATION SETUP */
    sa = dst->ai_addr;
    salen = dst->ai_addr->sa_len;
    m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr)+PFKEY_ALIGN8(salen));
    m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
    m_addr.sadb_address_proto = (unsigned int)IPSEC_ULPROTO_ANY;
    m_addr.sadb_address_prefixlen = HOST_MASK; /* (splen >= 0 ? -1 : plen ); */
    m_addr.sadb_address_reserved = 0;

    setvarbuf(buf, &m_size, (struct sadb_ext *)&m_addr,sizeof(m_addr),(caddr_t)sa, salen);

    msg->sadb_msg_len = PFKEY_UNIT64(m_size);

    (void)setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bufsiz, sizeof(bufsiz));
    (void)setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsiz, sizeof(bufsiz));

    rc = send(fd, buf, m_size, 0);
    if (rc < 0)
	perror("could not add entry to SAD: send()");

    free(buf);
    freeaddrinfo(src);
    freeaddrinfo(dst);
    return rc;
}

#else
/* The simple implementation based on keepalived draft */
void vrrp_ah_init_ahhdr(unsigned char *buffer, struct vrrp_vr *vr) {
    struct ip *ip;
    struct ah_header *ah;

    ip = (struct ip *) buffer;
    ah = (struct ah_header *) (buffer+sizeof(struct ip));
    ah->next = IPPROTO_VRRP;
    ah->length = 0x04;
    ah->zero = 0x0000;
    ah->spi = htonl(vr->vr_if->ip_addrs[0].s_addr);
    ah->seq = htonl(1);
    /* clean first */
    memset(ah->auth,0,sizeof(ah->auth));

    return;
}

void vrrp_ah_hmacmd5(unsigned char *buffer, struct vrrp_vr *vr) {
    struct ip *ip;
    struct ah_header *ah;
    unsigned char md5[16];

    ip = (struct ip *) buffer;
    ah = (struct ah_header *) (buffer+sizeof(struct ip));

    /* clear md5 */
    memset(md5,0,sizeof(md5));

    /* hexdump(md5,sizeof(md5)); */
    /* lets compute digest */
    hmac_md5(buffer, (sizeof(struct ip)+sizeof(struct ah_header)+sizeof(struct vrrp_hdr)), (unsigned char *)vr->password, strlen(vr->password), md5);
    /* hexdump(md5,sizeof(md5)); */

    /* copy it */
    memcpy(ah->auth, md5, 12);

    return;
}

/* return 0 if packet is valid, -1 else */
int vrrp_ah_check_ahhdr(char *buffer, struct vrrp_vr *vr) {
    struct ah_header *ah;
    unsigned char recv_authdata[HMAC_MD596_SIZE], comp_authdata[HMAC_MD596_SIZE+4];

    ah = (struct ah_header *) buffer;
    if (ah->next != IPPROTO_VRRP)
	return -1;
    if (ah->length != 0x04)
	return -1;
    /*
    if (ah->seq < vr->ahctx->seq) 
	return -1;
    else 
	vr->ahctx->seq = ah->seq;
	*/

    /* save auth data and rebuild hmac to see if it match */
    memcpy(recv_authdata,(caddr_t)ah->auth,HMAC_MD596_SIZE);
    memset(ah->auth,0, HMAC_MD596_SIZE);
    hmac_md5((unsigned char *)buffer, sizeof(struct ip)+sizeof(struct ah_header)+sizeof(struct vrrp_hdr), vr->password, strlen(vr->password), comp_authdata);

    if (memcmp(recv_authdata, comp_authdata, HMAC_MD596_SIZE) == 0)
	return 0;

    printf("packet invalid!!!\n");
    return -1;
}
#endif /* end of ifdef KAME_BASED */
#endif /* endof ENABLE_VRRP_AH */


int vrrp_ah_ahhdr_len(struct vrrp_vr *vr) {
#ifdef ENABLE_VRRP_AH
    if (vr->AHencryption == 1)
	return (sizeof(struct ah_header));
#endif
    return 0;
}


/*
RFC 2104 define this.
unsigned char*  text;                * pointer to data stream *
int             text_len;            * length of data stream *
unsigned char*  key;                 * pointer to authentication key *
int             key_len;             * length of authentication key *
caddr_t         digest;              * caller digest to be filled in *
*/
void hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len, caddr_t digest) {
        MD5_CTX context,tctx;
        unsigned char k_ipad[65];    /* inner padding - key XORd with ipad */
        unsigned char k_opad[65];    /* outer padding - key XORd with opad */
        unsigned char tk[16];
        int i;

        /* if key is longer than 64 bytes reset it to key=MD5(key) */
        if (key_len > 64) {

                MD5Init(&tctx);
                MD5Update(&tctx, key, key_len);
                MD5Final(tk, &tctx);

                key = tk;
                key_len = 16;
        }

        /*
         * the HMAC_MD5 transform looks like:
         *
         * MD5(K XOR opad, MD5(K XOR ipad, text))
         *
         * where K is an n byte key
         * ipad is the byte 0x36 repeated 64 times
         * opad is the byte 0x5c repeated 64 times
         * and text is the data being protected
         */

        /* start out by storing key in pads */
        bzero( k_ipad, sizeof k_ipad);
        bzero( k_opad, sizeof k_opad);
        bcopy( key, k_ipad, key_len);
        bcopy( key, k_opad, key_len);

        /* XOR key with ipad and opad values */
        for (i=0; i<64; i++) {
                k_ipad[i] ^= 0x36;
                k_opad[i] ^= 0x5c;
        }
        /* perform inner MD5 */
        MD5Init(&context);                   /* init context for 1st pass */
        MD5Update(&context, k_ipad, 64);      /* start with inner pad */
        MD5Update(&context, text, text_len); /* then text of datagram */
        MD5Final(digest, &context);          /* finish up 1st pass */

        /* perform outer MD5 */
	
        MD5Init(&context);                   /* init context for 2nd */

        MD5Update(&context, k_opad, 64);     /* start with outer pad */
        MD5Update(&context, digest, 16);     /* then results of 1st hash */
        MD5Final(digest, &context);          /* finish up 2nd pass */
}

/* Hexdumping on screen in a fancy format for debuging purposes */
int hexdump(unsigned char *zone, int len) {
    int rc=0,i;
    unsigned char *ptr;

    ptr = zone;
    fprintf(stderr,"-- hexdump at %p (%d bytes long) --",zone,len);
    for( i = 0 ;i < len;i++) {
	if((i%16)==0)
	    fprintf(stderr,"\n%p ",ptr+i);
	if((i%8)==0)
	    fprintf(stderr," ");
	fprintf(stderr,"0x%.2x ",*(ptr+i));
    }
    fprintf(stderr,"\n");
    return rc;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>