setkey.h 4.73 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 *	BIRD -- Manipulation the IPsec SA/SP database using setkey(8) utility
 *
 * 	(c) 2016 CZ.NIC z.s.p.o.
 */

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/pfkeyv2.h>
#include <netipsec/ipsec.h>

#include "nest/bird.h"
14
#include "sysdep/unix/unix.h"
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65


/*
 * Open a socket for manage the IPsec SA/SP database entries
 */
static int
setkey_open_socket(void)
{
  int s = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
  if (s < 0)
  {
    log(L_ERR "SETKEY: socket: %m");
    return -1;
  }

  return s;
}

static int
setkey_send(struct sadb_msg *msg, uint len)
{
  int s = setkey_open_socket();
  if (s < 0)
    return -1;

  if (msg->sadb_msg_type == SADB_ADD)
  {
    /* Delete possible current key in the IPsec SA/SP database */
    msg->sadb_msg_type = SADB_DELETE;
    send(s, msg, len, 0);
    msg->sadb_msg_type = SADB_ADD;
  }

  if (send(s, msg, len, 0) < 0)
  {
    log(L_ERR "SETKEY: send: %m");
    close(s);
    return -1;
  }

  close(s);
  return 0;
}

/*
 * Perform setkey(8)-like operation for set the password for TCP MD5 Signature.
 * Could be called with SABD_ADD or SADB_DELETE argument. Note that SADB_ADD
 * argument is internally processed as a pair of SADB_ADD and SADB_DELETE
 * operations to implement replace.
 */
static int
66
setkey_md5(sockaddr *src, sockaddr *dst, uint pxlen, char *passwd, uint type)
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
{
  uint passwd_len = passwd ? strlen(passwd) : 0;

  uint total =
    sizeof(struct sadb_msg) +
    sizeof(struct sadb_key) + PFKEY_ALIGN8(passwd_len) +
    sizeof(struct sadb_sa) +
    sizeof(struct sadb_x_sa2) +
    sizeof(struct sadb_address) + PFKEY_ALIGN8(src->sa.sa_len) +
    sizeof(struct sadb_address) + PFKEY_ALIGN8(dst->sa.sa_len);

  char *buf = alloca(total);
  char *pos = buf;
  uint len;

  memset(buf, 0, total);

  struct sadb_msg *msg = (void *) pos;
  len = sizeof(struct sadb_msg);
  msg->sadb_msg_version = PF_KEY_V2;
  msg->sadb_msg_type = type;
  msg->sadb_msg_satype = SADB_X_SATYPE_TCPSIGNATURE;
  msg->sadb_msg_len = 0;	/* Fix it later */
  msg->sadb_msg_pid = getpid();
  pos += len;

  /* Set authentication algorithm and password */
  struct sadb_key *key = (void *) pos;
  len = sizeof(struct sadb_key) + PFKEY_ALIGN8(passwd_len);
  key->sadb_key_len = PFKEY_UNIT64(len);
  key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
  key->sadb_key_bits = passwd_len * 8;
  memcpy(pos + sizeof(struct sadb_key), passwd, passwd_len);
  pos += len;

  struct sadb_sa *sa = (void *) pos;
  len = sizeof(struct sadb_sa);
  sa->sadb_sa_len = PFKEY_UNIT64(len);
  sa->sadb_sa_exttype = SADB_EXT_SA;
  sa->sadb_sa_spi = htonl((u32) TCP_SIG_SPI);
  sa->sadb_sa_auth = SADB_X_AALG_TCP_MD5;
  sa->sadb_sa_encrypt = SADB_EALG_NONE;
  sa->sadb_sa_flags = SADB_X_EXT_CYCSEQ;
  pos += len;

  struct sadb_x_sa2 *sa2 = (void *) pos;
  len = sizeof(struct sadb_x_sa2);
  sa2->sadb_x_sa2_len = PFKEY_UNIT64(len);
  sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
  sa2->sadb_x_sa2_mode = IPSEC_MODE_ANY;
  pos += len;

  /* Set source address */
  struct sadb_address *saddr = (void *) pos;
  len = sizeof(struct sadb_address) + PFKEY_ALIGN8(src->sa.sa_len);
  saddr->sadb_address_len = PFKEY_UNIT64(len);
  saddr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
  saddr->sadb_address_proto = IPSEC_ULPROTO_ANY;
125
  saddr->sadb_address_prefixlen = pxlen;
126 127 128 129 130 131 132 133 134
  memcpy(pos + sizeof(struct sadb_address), &src->sa, src->sa.sa_len);
  pos += len;

  /* Set destination address */
  struct sadb_address *daddr = (void *) pos;
  len = sizeof(struct sadb_address) + PFKEY_ALIGN8(dst->sa.sa_len);
  daddr->sadb_address_len = PFKEY_UNIT64(len);
  daddr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
  daddr->sadb_address_proto = IPSEC_ULPROTO_ANY;
135
  daddr->sadb_address_prefixlen = pxlen;
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
  memcpy(pos + sizeof(struct sadb_address), &dst->sa, dst->sa.sa_len);
  pos += len;

  len = pos - buf;
  msg->sadb_msg_len = PFKEY_UNIT64(len);

  return setkey_send(msg, len);
}

/*
 * Manipulation with the IPsec SA/SP database
 */
static int
sk_set_md5_in_sasp_db(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd)
{
  sockaddr src, dst;
  sockaddr_fill(&src, s->af, local, ifa, 0);
  sockaddr_fill(&dst, s->af, remote, ifa, 0);

155 156
  uint pxlen = (s->af == AF_INET) ? IP4_MAX_PREFIX_LENGTH : IP6_MAX_PREFIX_LENGTH;

157 158 159 160 161 162
  if (passwd && *passwd)
  {
    int len = strlen(passwd);
    if (len > TCP_KEYLEN_MAX)
      ERR_MSG("The password for TCP MD5 Signature is too long");

163 164
    if ((setkey_md5(&src, &dst, pxlen, passwd, SADB_ADD) < 0) ||
	(setkey_md5(&dst, &src, pxlen, passwd, SADB_ADD) < 0))
165 166 167 168
      ERR_MSG("Cannot add TCP-MD5 password into the IPsec SA/SP database");
  }
  else
  {
169 170
    if ((setkey_md5(&src, &dst, pxlen, NULL, SADB_DELETE) < 0) ||
	(setkey_md5(&dst, &src, pxlen, NULL, SADB_DELETE) < 0))
171 172 173 174
      ERR_MSG("Cannot delete TCP-MD5 password from the IPsec SA/SP database");
  }
  return 0;
}