]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BSD: Add the IPsec SA/SP database entries control
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Wed, 13 Apr 2016 12:30:28 +0000 (14:30 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Wed, 13 Apr 2016 12:37:09 +0000 (14:37 +0200)
Add code for manipulation with TCP-MD5 keys in the IPsec SA/SP database
at FreeBSD systems. Now, BGP MD5 authentication (RFC 2385) keys are
handled automatically on both Linux and FreeBSD.

Based on patches from Pavel Tvrdik.

doc/bird.sgml
lib/socket.h
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y
sysdep/bsd/Modules
sysdep/bsd/setkey.h [new file with mode: 0644]
sysdep/bsd/sysio.h
sysdep/linux/sysio.h
sysdep/unix/io.c

index 653e0bb5ef0ac23b2bf8bd74582d111da18dd356..1a5fbaff9f188828e90a6b05f2e668b3f6f4a00a 100644 (file)
@@ -1764,9 +1764,20 @@ using the following configuration parameters:
        only. Default: disabled.
 
        <tag>password <m/string/</tag>
-       Use this password for MD5 authentication of BGP sessions. Default: no
-       authentication. Password has to be set by external utility
-       (e.g. setkey(8)) on BSD systems.
+       Use this password for MD5 authentication of BGP sessions (RFC 2385).
+       When used on BSD systems, see also <cf/setkey/ option below. Default:
+       no authentication.
+
+       <tag>setkey <m/switch/</tag>
+       On BSD systems, keys for TCP MD5 authentication are stored in the global
+       SA/SP database, which can be accessed by external utilities (e.g.
+       setkey(8)). BIRD configures security associations in the SA/SP database
+       automatically based on <cf/password/ options (see above), this option
+       allows to disable automatic updates by BIRD when manual configuration by
+       external utilities is preferred. Note that automatic SA/SP database
+       updates are currently implemented only for FreeBSD. Passwords have to be
+       set manually by an external utility on NetBSD and OpenBSD. Default:
+       enabled (ignored on non-FreeBSD).
 
        <tag>passive <m/switch/</tag>
        Standard BGP behavior is both initiating outgoing connections and
index 0327e9e5bc24815912379bca729ede132d1b299c..6babfa7bc5ecc00e56cfc76c6b3e4ec06252e4dc 100644 (file)
@@ -87,7 +87,7 @@ int sk_leave_group(sock *s, ip_addr maddr);   /* Leave multicast group on sk iface
 int sk_setup_broadcast(sock *s);
 int sk_set_ttl(sock *s, int ttl);      /* Set transmit TTL for given socket */
 int sk_set_min_ttl(sock *s, int ttl);  /* Set minimal accepted TTL for given socket */
-int sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd);
+int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd, int setkey);
 int sk_set_ipv6_checksum(sock *s, int offset);
 int sk_set_icmp6_filter(sock *s, int p1, int p2);
 void sk_log_error(sock *s, const char *p);
index 94c8e5c24df40da5a72265a5ba71db4a779232ee..2014525e49de1ff17d3b4c743d62ea793364e5f0 100644 (file)
@@ -121,7 +121,8 @@ bgp_open(struct bgp_proto *p)
   bgp_counter++;
 
   if (p->cf->password)
-    if (sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, p->cf->password) < 0)
+    if (sk_set_md5_auth(bgp_listen_sk, p->cf->source_addr, p->cf->remote_ip,
+                       p->cf->iface, p->cf->password, p->cf->setkey) < 0)
       {
        sk_log_error(bgp_listen_sk, p->p.name);
        bgp_close(p, 0);
@@ -191,7 +192,8 @@ bgp_close(struct bgp_proto *p, int apply_md5)
   bgp_counter--;
 
   if (p->cf->password && apply_md5)
-    if (sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, NULL) < 0)
+    if (sk_set_md5_auth(bgp_listen_sk, p->cf->source_addr, p->cf->remote_ip,
+                       p->cf->iface, NULL, p->cf->setkey) < 0)
       sk_log_error(bgp_listen_sk, p->p.name);
 
   if (!bgp_counter)
index 274794f136c8e2e208911165a59b531ba0acceea..b1cca2d9994e5225051ddff2d525b4985945d5e8 100644 (file)
@@ -51,6 +51,7 @@ struct bgp_config {
   int add_path;                                /* Use ADD-PATH extension [draft] */
   int allow_local_as;                  /* Allow that number of local ASNs in incoming AS_PATHs */
   int gr_mode;                         /* Graceful restart mode (BGP_GR_*) */
+  int setkey;                          /* Set MD5 password to system SA/SP database */
   unsigned gr_time;                    /* Graceful restart timeout */
   unsigned connect_delay_time;         /* Minimum delay between connect attempts */
   unsigned connect_retry_time;         /* Timeout for connect attempts */
index 85b93a6b9e5f8dc6de62a11bb9236354b5f857a8..f3ba0e16b72437d014601d8bec892f60c2b697e4 100644 (file)
@@ -27,7 +27,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
        INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
        TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
        SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX, GRACEFUL, RESTART, AWARE,
-       CHECK, LINK, PORT, EXTENDED, MESSAGES)
+       CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY)
 
 CF_GRAMMAR
 
@@ -54,6 +54,7 @@ bgp_proto_start: proto_start BGP {
      BGP_CFG->default_local_pref = 100;
      BGP_CFG->gr_mode = BGP_GR_AWARE;
      BGP_CFG->gr_time = 120;
+     BGP_CFG->setkey = 1;
  }
  ;
 
@@ -112,6 +113,7 @@ bgp_proto:
  | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
  | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; }
  | bgp_proto PASSWORD text ';' { BGP_CFG->password = $3; }
+ | bgp_proto SETKEY bool ';' { BGP_CFG->setkey = $3; }
  | bgp_proto ROUTE LIMIT expr ';' {
      this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit));
      this_proto->in_limit->limit = $4;
index 96455db7a5d238989ac57ad59d3084a21383bc13..39db88e98af75c6b725cb9848e8809940c80603d 100644 (file)
@@ -2,3 +2,4 @@ krt-sock.c
 krt-sock.Y
 krt-sys.h
 sysio.h
+setkey.h
diff --git a/sysdep/bsd/setkey.h b/sysdep/bsd/setkey.h
new file mode 100644 (file)
index 0000000..b417fac
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ *     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"
+#include "lib/unix.h"
+
+
+/*
+ * 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
+setkey_md5(sockaddr *src, sockaddr *dst, char *passwd, uint type)
+{
+  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;
+  saddr->sadb_address_prefixlen = MAX_PREFIX_LENGTH;
+  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;
+  daddr->sadb_address_prefixlen = MAX_PREFIX_LENGTH;
+  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);
+
+  if (passwd && *passwd)
+  {
+    int len = strlen(passwd);
+    if (len > TCP_KEYLEN_MAX)
+      ERR_MSG("The password for TCP MD5 Signature is too long");
+
+    if (setkey_md5(&src, &dst, passwd, SADB_ADD) < 0)
+      ERR_MSG("Cannot add TCP-MD5 password into the IPsec SA/SP database");
+  }
+  else
+  {
+    if (setkey_md5(&src, &dst, NULL, SADB_DELETE) < 0)
+      ERR_MSG("Cannot delete TCP-MD5 password from the IPsec SA/SP database");
+  }
+  return 0;
+}
index c82d7a1e560d8c1941cd8b49a892380525866b4a..6c20733fda5ce5e1017ce4bd36cb28e352cb9123 100644 (file)
@@ -189,30 +189,26 @@ sk_prepare_ip_header(sock *s, void *hdr, int dlen)
 #ifndef TCP_KEYLEN_MAX
 #define TCP_KEYLEN_MAX 80
 #endif
+
 #ifndef TCP_SIG_SPI
 #define TCP_SIG_SPI 0x1000
 #endif
 
-/* 
- * FIXME: Passwords has to be set by setkey(8) command. This is the same
- * behaviour like Quagga. We need to add code for SA/SP entries
- * management.
- */
+#if defined(__FreeBSD__)
+#define USE_MD5SIG_SETKEY
+#include "lib/setkey.h"
+#endif
 
 int
-sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd)
+sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd, int setkey UNUSED)
 {
-  int enable = 0;
-
-  if (passwd && *passwd)
-  {
-    int len = strlen(passwd);
-    enable = TCP_SIG_SPI;
-
-    if (len > TCP_KEYLEN_MAX)
-      ERR_MSG("MD5 password too long");
-  }
+#ifdef USE_MD5SIG_SETKEY
+  if (setkey)
+    if (sk_set_md5_in_sasp_db(s, local, remote, ifa, passwd) < 0)
+      return -1;
+#endif
 
+  int enable = (passwd && *passwd) ? TCP_SIG_SPI : 0;
   if (setsockopt(s->fd, IPPROTO_TCP, TCP_MD5SIG, &enable, sizeof(enable)) < 0)
   {
     if (errno == ENOPROTOOPT)
index c1561cbf17c212090aecc3e6b46968b14cc7ebcb..58644417f9cffda0ea6154f76e0ea94b87f31b4d 100644 (file)
@@ -179,19 +179,19 @@ sk_prepare_cmsgs4(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen)
  */
 
 int
-sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd)
+sk_set_md5_auth(sock *s, ip_addr local UNUSED, ip_addr remote, struct iface *ifa, char *passwd, int setkey UNUSED)
 {
   struct tcp_md5sig md5;
 
   memset(&md5, 0, sizeof(md5));
-  sockaddr_fill((sockaddr *) &md5.tcpm_addr, s->af, a, ifa, 0);
+  sockaddr_fill((sockaddr *) &md5.tcpm_addr, s->af, remote, ifa, 0);
 
   if (passwd)
   {
     int len = strlen(passwd);
 
     if (len > TCP_MD5SIG_MAXKEYLEN)
-      ERR_MSG("MD5 password too long");
+      ERR_MSG("The password for TCP MD5 Signature is too long");
 
     md5.tcpm_keylen = len;
     memcpy(&md5.tcpm_key, passwd, len);
index 5955dbfef52fd8efbcfa3ccbc6d1fc292c8e9229..d918d321ee0428d3442726f055dece7865951b63 100644 (file)
@@ -951,23 +951,32 @@ sk_set_min_ttl(sock *s, int ttl)
 /**
  * sk_set_md5_auth - add / remove MD5 security association for given socket
  * @s: socket
- * @a: IP address of the other side
+ * @local: IP address of local side
+ * @remote: IP address of remote side
  * @ifa: Interface for link-local IP address
- * @passwd: password used for MD5 authentication
+ * @passwd: Password used for MD5 authentication
+ * @setkey: Update also system SA/SP database
  *
- * In TCP MD5 handling code in kernel, there is a set of pairs (address,
- * password) used to choose password according to address of the other side.
- * This function is useful for listening socket, for active sockets it is enough
- * to set s->password field.
+ * In TCP MD5 handling code in kernel, there is a set of security associations
+ * used for choosing password and other authentication parameters according to
+ * the local and remote address. This function is useful for listening socket,
+ * for active sockets it may be enough to set s->password field.
  *
  * When called with passwd != NULL, the new pair is added,
  * When called with passwd == NULL, the existing pair is removed.
  *
+ * Note that while in Linux, the MD5 SAs are specific to socket, in BSD they are
+ * stored in global SA/SP database (but the behavior also must be enabled on
+ * per-socket basis). In case of multiple sockets to the same neighbor, the
+ * socket-specific state must be configured for each socket while global state
+ * just once per src-dst pair. The @setkey argument controls whether the global
+ * state (SA/SP database) is also updated.
+ *
  * Result: 0 for success, -1 for an error.
  */
 
 int
-sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd)
+sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd, int setkey)
 { DUMMY; }
 #endif
 
@@ -1437,7 +1446,7 @@ sk_open(sock *s)
   }
 
   if (s->password)
-    if (sk_set_md5_auth(s, s->daddr, s->iface, s->password) < 0)
+    if (sk_set_md5_auth(s, s->saddr, s->daddr, s->iface, s->password, 0) < 0)
       goto err;
 
   switch (s->type)